Security Posture¶
Document: docs/product/security-posture.md
Status: Canonical — all briefs touching auth, routing, data exposure, or system access must conform to this document
Last updated: 2026-04-06
Authority: Tim Rignold, RTOpacks Pty Ltd
Philosophy¶
RTOpacks is a data business. The competitive moat is the data — 15,128 enriched units, 6,187,511 rows of RTO scope data, 12,508 RTO intelligence profiles, labour market enrichment, career pathway data. This corpus was built at extraordinary effort and cost. It is the asset. It must be treated as such.
RTOpacks owns and operates its own security posture. It does not inherit or delegate security to any external party.
The governing principle is: never expose more than the user has earned the right to see, and log everything.
The Moat/Mode Distinction¶
Every architectural and product decision must be evaluated against this question: does this expose moat or mode?
- Exposing mode is acceptable — the intelligence features, the KN reasoning, the guided workflows are what we show to attract and retain users.
- Exposing moat is a controlled, deliberate act — specific data, to specific users, for specific purposes, with full logging. Never accidental, never by default.
Authentication¶
Mechanism: Magic link via Resend (noreply@rtopacks.com.au). No passwords. No social login.
Access control: Invite-only. The access_allowlist table in ops-db gates all magic link delivery. Unlisted emails receive no link and no confirmation. Timing-attack resistant (fixed 800ms response delay on both paths).
Session: RTPSession stored in KV with 24-hour sliding TTL. Cookie: rtp_session (HttpOnly, Secure, SameSite=Strict).
Auth events logged to workspace-db: link requested, link used, session started. Every event includes timestamp, user, IP.
Rejection logging: Every blocked magic link attempt logged to auth_rejection_log in ops-db with IP and email domain (not full email).
Tier Model¶
RTOpacks implements the UCCA five-layer access model:
| Layer | Name | Description | Present in RTOpacks |
|---|---|---|---|
| L4A (4.5) | Members | Individual staff within an RTO client org | Yes — default for new users |
| L4 | Client Organisation | RTO administrators | Yes — resolved from org + group membership |
| L3 | RTOpacks Operators | admin@rtopacks.com.au and named staff |
Yes — via allowlist + org lookup |
| L1/L2 | UCCA Infrastructure | Reserved for future federation | Not present — FEDERATION-HOOK-01 placeholder |
Tier is resolved at login by resolveUserTier() and stored in the session. Not recalculated per-request. See auth-architecture.md for full flow.
Data Access Gate (API-GATE-01)¶
internal-api.rtopacks.com.au is the sole access point for rtopacks-db. No surface queries rtopacks-db directly.
- Every request authenticated via dual-layer auth (CF Access perimeter + X-RTP-Session application token)
- Every request logged to
api_access_login ops-db with user_id, org_id, ucca_layer, endpoint, method, params, response status, session_id - KN content (
kn_criteria_reasoning) gated — only returned to L1-L4, stripped from L4A responses
PIPELINE-EXEMPT register — 5 files retain direct rtopacks-db access for corpus ingestion/analytics. Grep PIPELINE-EXEMPT for the complete list:
1. api/enrich/route.js — corpus enrichment pipeline
2. api/search-enrich/route.js — search-triggered enrichment
3. api/subscribe/route.js — subscribers table
4. api/qual-detail/route.js — qual_page_views analytics
5. api/nrt/route.js — api_requests logging
Anomaly Detection (SEC-02)¶
Three-zone model on internal-api. Protects against authenticated users wiring session tokens into automated agents.
| Zone | Behaviour | Threshold |
|---|---|---|
| Green | Normal — no action | ≤ 30 req / 10-min window |
| Amber | Silent friction — 800–1200ms randomised latency injected post-handler | Velocity >30/10min, sequential traversal ≥15 codes, timing stddev <50ms, breadth >8 groups/5min |
| Red | Session revoked — 401 identical to normal expiry. Admin emailed. | Velocity >90/10min, sequential >100 codes, or amber across ≥2 sessions/24hr |
L1/L2 bypass: UCCA-tier sessions skip anomaly check entirely.
Logged to anomaly_log in ops-db on every zone transition. Red zone sends email to admin@rtopacks.com.au via Resend.
Scraping Defence¶
What We Protect¶
The NRT corpus as structured and enriched in RTOpacks. The raw TGA data is public (CC BY 4.0). What is not public: - The structure, relationships, and enrichment applied to that data - The KN reasoning layer - The market intelligence enrichment - The RTO intelligence corpus (Radar/Landscape) - The scope mapping at scale (rto_scope_v2)
Defence Mechanisms¶
URL design: No enumerable public URL patterns. No sitemap of data object URLs.
Rate limiting: Applied at Worker layer and API layer independently. Waitlist lookup: 10/IP/hour. Internal-api: per-session anomaly detection.
Behavioural anomaly detection (SEC-02): Authenticated sessions monitored for agent-signature patterns. Anomalous sessions enter silent friction zone. Confirmed hostile sessions revoked.
Per-render tokens (SEC-01): Not yet built. Authenticated pages will embed session-specific tokens traceable to the rendering user. Specified, not implemented.
Canary values (SEC-01): Not yet built. Subtle per-session data variations for source identification. Specified, not implemented.
No bulk endpoints: No API endpoint returns unbounded result sets. All queries paginated and scoped.
Audit Trail Architecture¶
Everything that happens leaves a traceable record.
Authentication events: workspace-db — link requested, link used, session started, failed attempts.
API access: ops-db api_access_log — every internal-api request with user, org, tier, endpoint, params, status.
Anomaly events: ops-db anomaly_log — every zone transition with full state snapshot.
Rejections: ops-db auth_rejection_log — every blocked magic link attempt.
Mode switches: ops-db mode_switch_log — every LIVE/GUIDED/COMPLIANCE transition.
Export and output events: ops-db — every PDF, print, CSV, JSON export (when implemented).
L4 management visibility: L4 org owners can see full activity logs for all L4A members in their org.
Export Format Policy¶
| Format | Status | Notes |
|---|---|---|
| Enabled — primary format | ASQA-ready | |
| Word (.docx) | Built, not exposed at launch | Explicit product decision to enable |
| Excel (.xlsx) | Built, not exposed at launch | Explicit product decision to enable |
| CSV | Built, not exposed at launch | Leaky port — explicit Tim sign-off required |
| JSON | Built, not exposed at launch | Leaky port — explicit Tim sign-off required |
What RTOpacks Is Not¶
- RTOpacks is not an alternate to training.gov.au
- RTOpacks does not submit data object URLs to search engines without explicit sign-off
- RTOpacks does not provide open API access to NRT data
- RTOpacks does not expose raw database exports through any user-facing surface
Forward Flags¶
| Flag ID | Description | Trigger condition |
|---|---|---|
| INFRA-TF-01 | Separate RTOpacks Terraform state from UCCA Terraform state | When RTOpacks gets its own Cloudflare account |
| CSP-NONCE-01 | Full nonce-based CSP implementation | Scheduled brief |
| SEC-01 | Per-session attribution tokens and canary values | Before public launch |
| API-GATE-01 | Route all NRT reads through internal-api | ✓ Complete |
| DROP-STALE-01 | Drop stale ops-db tables | After confirmed clean operation |
| ROLES-01 | Define L4 group permissions model in full | Before first L4 client onboarding |
| FEDERATION-HOOK-01 | L2↔L3 assertion token — UCCA OAuth server | When B-AUTH-OAUTH-01 ships |
Security Brief Register¶
| Brief ID | Description | Status |
|---|---|---|
| SEC-01 | Per-session token implementation, canary values, scraping defence technical spec | Not yet written |
| SEC-02 | Behavioural anomaly detection — three-zone model, silent friction, session revocation | Shipped |
| SEC-03 | Passkeys + UCCA Authenticator native app — WebAuthn, step-up re-auth, biometric attribution | Not yet written |
| SEO-01 | Strategic SEO approach — how to capture search intent without exposing the moat | Not yet written |
| FOUNDATION-02 | Define --rp-tag-* and --rp-category-* functional colour tokens |
Not yet written |
Amendment Log¶
| Date | Change | Authority |
|---|---|---|
| 2026-04-06 | Initial document — Session 41/42 architectural sprint | Tim Rignold |
| 2026-04-06 | Updated auth boundary, log storage split, forward flags | Tim Rignold |
| 2026-04-06 | Complete rewrite — standalone RTOpacks perspective (DOCS-02, Session 44) | Tim Rignold |