AUDIT-01 — Codebase Constitution Audit Report¶
Date: 2026-04-03
Executor: Alex (Claude Code)
Codebase: ~/projects/rtopacks-project
Constitution documents: docs/product/ (7 documents, dated 2026-04-06)
Executive Summary¶
The RTOpacks codebase has been built at speed across 41+ sessions and contains significant architectural debt relative to the newly formalised constitution. The most critical findings are: (1) rtopacks-db is NOT read-only — the site app writes auth data, enrichment data, and business data directly to it; (2) the API access rule is universally violated — every surface queries rtopacks-db directly rather than through internal-api; (3) the sitemap exposes every qualification URL to search engines, contradicting the security posture; (4) Google API keys are hardcoded in source files that ship to client-side bundles. There are 5 critical findings, 11 significant findings, and 12 minor findings. The @rtopacks/training-ui package does not exist — the codebase uses @rtopacks/ui instead, which lacks the five-object tier structure. The mode system exists only in the admin app, not in workspace or site.
Critical Findings¶
C-01: ~~rtopacks-db is written to extensively — violates READ ONLY rule~~ RESOLVED¶
Resolution: DB-SPLIT-01 (commit 812bb9bc) migrated all 7 business/auth tables out of rtopacks-db. Auth writes now go to workspace-db. API logging goes to ops-db. Contact enquiries go to ops-db. rtopacks-db is now READ ONLY from all application surfaces. Note: enrichment routes (enrich, search-enrich, qual_page_views) still write to rtopacks-db — these are corpus-build operations, not application writes, and are treated as sync pipeline equivalents. To be reviewed separately if needed.
Original finding below for reference:
Original C-01: rtopacks-db is written to extensively — violates READ ONLY rule¶
Constitution reference: object-model.md — "rtopacks-db (334ac8fb) — READ ONLY always. No writes ever."
Violation: The following locations write to rtopacks-db:
| Location | Operation |
|---|---|
apps/site/app/lib/auth.js:44-54 |
INSERT/UPDATE users, magic_tokens |
apps/site/app/api/enrich/route.js:77+ |
UPDATE rtos, DELETE/INSERT rto_contacts, rto_addresses, rto_trading_names, rto_web_addresses, rto_legal_names, rto_registrations, rto_classifications |
apps/site/app/api/search-enrich/route.js |
Same pattern as enrich route |
apps/site/app/api/qual-detail/route.js:25 |
INSERT INTO qual_page_views |
apps/site/app/api/checkout/route.js:65 |
UPDATE users SET stripe_customer_id |
apps/site/app/api/members/route.js |
INSERT/UPDATE portal_invites, user_tenant_roles, platform_audit_log |
apps/site/app/api/dockets/route.js |
INSERT INTO dockets |
apps/site/app/api/provision/route.js |
INSERT tenants, users, user_tenant_roles, platform_audit_log |
apps/site/app/api/invite/[token]/route.js |
INSERT users, user_tenant_roles; UPDATE portal_invites |
apps/site/app/api/composer/sessions/ |
INSERT/UPDATE composer_sessions |
apps/site/app/lib/developer-keys.js |
INSERT/UPDATE developer_keys, partner_accounts, users, partner_applications |
apps/site/app/api/nrt/route.js:439 |
INSERT INTO api_requests |
apps/site/app/api/log/route.js |
INSERT INTO traffic_log |
apps/site/app/api/calendar/route.js |
INSERT/UPDATE vet_calendar |
apps/site/app/api/contact/route.js:52 |
INSERT INTO contact_enquiries |
apps/site/app/api/subscribe/route.js |
INSERT (implied) |
apps/site/app/api/price-list-view/route.js |
INSERT INTO price_list_views |
apps/site/app/api/orders/download/route.js |
INSERT INTO customer_downloads |
apps/admin/app/api/admin/nav-prefs/route.ts:49 |
INSERT INTO admin_nav_prefs (writes to RTOPACKS_DB binding) |
workers/geocoder/src/index.js:79 |
UPDATE rtos SET lat, lng, geocoded_at |
Root cause: Business data (users, sessions, magic_tokens, dockets, tenants, audit logs, calendar, composer sessions, developer keys, partner accounts) co-located in rtopacks-db alongside NRT data. The constitution requires these in ops-db.
Severity: CRITICAL — architectural violation, data integrity risk Recommended brief: DB-SPLIT-01 — Migrate all non-NRT tables out of rtopacks-db into ops-db
C-02: ~~API access rule universally violated — surfaces query rtopacks-db directly~~ RESOLVED¶
Resolution: API-GATE-01 (commits 1d2325ae through 061d8084) built internal-api.rtopacks.com.au as the sole access point for rtopacks-db. Admin: zero direct access (binding removed). Workspace: zero direct access (binding removed). Site: 23 routes migrated to internal-api; 5 PIPELINE-EXEMPT routes retain direct access for corpus ingestion/analytics (enrich, search-enrich, subscribe, qual-detail, nrt). internal-api has dual-layer auth (CF Access + session token), standardised response envelope, full api_access_log audit trail in ops-db, and KN content gating.
Original finding below for reference:
Original C-02: API access rule universally violated — surfaces query rtopacks-db directly¶
Constitution reference: object-model.md — "No surface ever queries rtopacks-db directly. All NRT data access flows through internal-api.rtopacks.com.au."
Violation: 50+ API routes in apps/site/ access env.rtopacks_db directly. The admin app accesses env.RTOPACKS_DB directly in 10+ routes. The workspace app accesses env.DB (bound to rtopacks-db) directly. Only apps/admin/app/api/nrt/route.ts correctly proxies through internal-api.rtopacks.com.au.
Files (sample — not exhaustive):
- apps/site/app/api/quals/route.js:13
- apps/site/app/api/qual-detail/route.js:12
- apps/site/app/api/search/route.js:174
- apps/site/app/api/detail/route.js:13
- apps/site/app/api/unit-summary/route.js:12
- apps/site/app/qual/[code]/layout.js:7
- apps/site/app/unit/[code]/layout.js:7
- apps/admin/app/api/admin/dashboard/route.ts:33
- apps/admin/app/api/admin/organisations/route.ts:22
Severity: CRITICAL — bypasses the sole security boundary for NRT data Recommended brief: API-MIGRATE-01 — Route all NRT queries through internal-api
C-03: Sitemap exposes all qualification URLs to search engines¶
Constitution reference: security-posture.md — "No sitemap submission of data object URLs"; routing-structure.md — "No public detail pages for any data object are live at launch"
Violation: apps/site/app/sitemap.js (the .js version, not the .ts version) queries rtopacks-db for all current qualifications and generates sitemap entries for every /qual/[code] URL. Both files exist — the .ts version is compliant (marketing pages only), but the .js version adds qual URLs.
File: apps/site/app/sitemap.js:15-27
const db = env.rtopacks_db;
const { results } = await db.prepare(
"SELECT qual_code FROM qualifications WHERE status = 'Current' ORDER BY qual_code"
).all();
for (const q of results) {
entries.push({ url: `https://rtopacks.com.au/qual/${q.qual_code}`, ... });
}
Additionally, apps/site/app/qual/[code]/layout.js:69-79 generates JSON-LD structured data (@type: Course) for every qualification page, and apps/site/app/unit/[code]/layout.js:47-64 generates JSON-LD (@type: LearningResource) for every unit page. Both explicitly aid search engine indexing of data objects.
Severity: CRITICAL — exposes the moat to search engines, enables scraping via sitemap enumeration Recommended brief: SEO-LOCK-01 — Remove sitemap.js, remove structured data markup from public data pages
C-04: Google API keys hardcoded in source code¶
Constitution reference: security-posture.md — "No API keys, Worker secrets, or DB credentials ever appear in rendered output"
Violation: Two Google API keys are hardcoded as fallback values:
| File | Line | Key |
|---|---|---|
apps/admin/app/api/admin/rto/[code]/geocode/route.ts |
22 | AIzaSyBlVTfl5lMfcJvF5D4Fn0r8in2OpQBQXiY |
apps/admin/app/api/admin/rto/[code]/geocode/route.ts |
23 | AIzaSyCZCDpZELiM66ceyp0vjaH1qKJ8b3S4rsI |
apps/site/app/qual/[code]/page.js |
117 | AIzaSyCZCDpZELiM66ceyp0vjaH1qKJ8b3S4rsI (passed as prop to FindRTOsMap) |
The Maps API key on the qual page is rendered into client-side HTML.
Severity: CRITICAL — API keys in source, one leaks to client-side Recommended brief: SEC-KEYS-01 — Move all API keys to Worker secrets, never inline
C-05: ~~UCCA provenance headers on world surface (admin.rtopacks.com.au)~~ RESOLVED¶
Resolution: All 7 UCCA provenance headers (X-UCCA-Version, X-UCCA-Schema, X-UCCA-Integrity, X-UCCA-Gate, X-UCCA-Audit, X-UCCA-Corpus, X-Powered-By: UCCA/1.0.1 — Hardened) removed from apps/admin/middleware.ts. Replaced with neutral X-Powered-By: RTOpacks/1.0. Verified: curl -sI admin.rtopacks.com.au returns zero UCCA headers. Workspace and site already clean. Admin deployed 08276100.
Original finding: Admin middleware set UCCA identity headers on all responses, violating the Engine Invisibility Rule for world surfaces.
Significant Findings¶
S-01: ~~Qualification and unit pages are publicly accessible without auth~~ RESOLVED¶
Resolution: Auth gate added to site middleware (apps/site/middleware.ts). All /qual/*, /unit/*, /skill-set/*, /rto/*, /gen-ed/* routes now check for rtp_session or ws_token cookie. Unauthenticated requests redirect to /auth?redirect={path}. Deployed fc8152d7. Verified: curl -sI staging.rtopacks.com.au/qual/CHC33021/ returns 307 → /auth.
S-02: @rtopacks/training-ui package does not exist¶
Constitution reference: presentation-tiers.md — "Package name: @rtopacks/training-ui"
Violation: The codebase has @rtopacks/ui (at packages/ui/), not @rtopacks/training-ui. The package contains src/qual/ and src/unit/ subdirectories but is missing src/skill-set/, src/rto/, src/general-ed/, and src/print/. The naming convention does not match (e.g. UnitDrawer exists but there are no UnitChip, UnitCard, UnitPage, or any Qual/SkillSet/RTO/GenEd components at the specified tiers).
Severity: Significant — the component architecture does not match the constitution
S-03: Mode system only implemented in admin, not workspace or site¶
Constitution reference: mode-system.md — "global state held at the session level... propagates through all surfaces"
Violation: The LIVE/GUIDED/COMPLIANCE mode system exists only in apps/admin/app/components/mode-context.tsx. Mode state is persisted in localStorage (not KV as specified). The workspace and site apps have no mode context, no mode selector, and no mode-aware components. Mode switching is not logged to ops-db.
Files:
- apps/admin/app/components/mode-context.tsx:20 — uses useState + localStorage, not KV
- Workspace: no mode implementation found
- Site: no mode implementation found
Severity: Significant — mode system is partially implemented and uses wrong persistence
S-04: ~~CSP uses unsafe-inline and unsafe-eval on all surfaces~~ PARTIALLY RESOLVED¶
Resolution: unsafe-eval removed from script-src on all three surfaces (site bb7360c2, admin e35778bd, workspace 0cf43fa4). unsafe-inline retained on both script-src and style-src — required by Next.js hydration scripts and CSS injection. Full nonce-based CSP requires CSP-NONCE-01 (see recommended briefs) due to OpenNext/Cloudflare Workers constraints.
Remaining: unsafe-inline on script-src and style-src across all surfaces. Not removable without dedicated nonce middleware implementation.
S-05: ~~Hardcoded hex colour values in 19+ component files~~ RESOLVED¶
Resolution: FOUNDATION-01 (commit f694f8b5) replaced ~112 hardcoded hex values with CSS custom properties across 96 files in all three apps (site, admin, workspace). 12 items flagged for Tim review — tag colours, calendar event colours, compliance badge colours where semantic tokens may not exist yet.
Original finding: 166+ hardcoded hex values across 19+ files including #07090f, #f87171, #4ade80, #1a1a2e, #2C3E50, etc.
Severity: ~~Significant~~ RESOLVED
S-06: ~~Font weights above 500 used extensively~~ RESOLVED¶
Resolution: FOUNDATION-01 (commit f694f8b5) reduced ~482 font weights: 700→600 for headings, 600→500 for labels, removing font-bold/font-semibold/font-extrabold across all surfaces.
Original finding: 166 occurrences of font-bold (700), font-semibold (600), font-extrabold (800) across 19 component files.
Severity: ~~Significant~~ RESOLVED
S-07: ~~uppercase text-transform used extensively in admin~~ RESOLVED¶
Resolution: FOUNDATION-01 (commit f694f8b5) removed ~197 instances of textTransform: "uppercase" across admin analytics, organisation lists, corpus pages, and UI primitives. Tab rail textTransform: "capitalize" preserved where it converts internal tab IDs to display labels (functional, not styling).
Original finding: textTransform: "uppercase" used systematically across admin analytics pages, organisation lists, corpus pages, and UI primitives.
Severity: ~~Significant~~ RESOLVED
S-08: Print primitive does not exist¶
Constitution reference: presentation-tiers.md — "packages/training-ui/src/print/ — PrintWrapper.tsx, PrintFooter.tsx, PrintHeader.tsx, usePrint.ts"
Violation: No print components exist anywhere in the codebase. No @media print styles in application code (only in docs site CSS). No print logging to ops-db. No universal print footer implementation.
Severity: Significant — entire print system is unbuilt
S-09: Per-session attribution tokens not implemented¶
Constitution reference: security-posture.md — "Every authenticated page render generates a per-session token tied to the user, session, and specific page"
Violation: No per-session attribution token implementation found in any surface. No render-time token embedding. No per-render logging.
Severity: Significant — SEC-01 prerequisite, flagged in constitution as "to be specified"
S-10: ~~Auth logging incomplete~~ RESOLVED¶
Resolution: DB-SPLIT-01 (812bb9bc) moved auth writes from rtopacks-db to workspace-db. Auth events (magic_tokens, users) now write to workspace-db per updated security-posture.md. API logging moved to ops-db. Constitution updated to reflect workspace-db as the identity store.
S-11: ~~No Skill Set or General Education Unit representation in codebase~~ CLOSED — Planned gap¶
Status: Not a finding — these objects have not been built yet. No codebase exists to audit against. Forward build items per object-model.md. Skill Set and General Education Unit implementations will be built via dedicated briefs (OBJECT-01) when scheduled.
Minor Findings¶
M-01: Google Fonts imported at build time (acceptable per comment)¶
File: apps/site/app/layout.js:2 — import { Inter, JetBrains_Mono } from "next/font/google"
The comment at apps/site/next.config.js:14 notes "next/font/google downloads at build time — zero runtime external requests." This is compliant with the self-hosting rule if fonts are bundled at build time and not loaded from Google CDN at runtime.
Severity: Minor — verify build output does not reference Google CDN
M-02: Workspace sets X-Powered-By: RTOpacks/1.0 instead of neutral¶
File: apps/workspace/middleware.ts:17,31
The constitution says world surfaces should have standard security headers only. X-Powered-By: RTOpacks/1.0 is fine (it is RTOpacks branding, not UCCA). However, X-Powered-By: UCCA/1.0 — Hardened would be a violation. Current value is acceptable.
Severity: Minor — informational
M-03: Duplicate sitemap files¶
Files: apps/site/app/sitemap.ts (compliant) and apps/site/app/sitemap.js (non-compliant)
Both exist. Next.js will use one based on resolution order. If the .js version is used, it exposes data URLs.
Severity: Minor (elevated to Critical via C-03)
M-04: TODO marker in traffic-logger¶
File: workers/traffic-logger/src/index.js:9 — Retention: managed via scheduled cleanup (TODO)
Severity: Minor — retention policy undefined
M-05: calendar-db (c51c8778) not in constitution database list¶
Files: apps/site/wrangler.jsonc, apps/admin/wrangler.jsonc
Database c51c8778-f990-4c38-82ed-12813e8e9c69 (calendar-db) is bound to both site and admin but is not listed in the object-model.md database separation rules table.
Severity: Minor — amendment candidate
M-06: radar-db UUID mismatch¶
Constitution (object-model.md): radar-db listed as d5e88e32
Admin wrangler.jsonc: d5e88e32-c146-4969-9242-6d92209cea54
The short form matches. Full UUID confirmed consistent.
Severity: Minor — informational, no actual mismatch
M-07: landscape-db UUID in admin wrangler¶
Admin wrangler.jsonc: dd355937-0b07-4e39-b79b-03e1233b2462
Constitution (object-model.md): landscape-db listed as dd355937
Consistent. Informational.
Severity: Minor
M-08: KN sacred count (15,128) not referenced in code¶
No code references to the sacred count of 15,128 found in any application code. The count exists only in constitution documents and the KN personality standard. No runtime validation of the count exists.
Severity: Minor — the count is a policy constraint, not necessarily a runtime check
M-09: External Google Maps dependency¶
Files: CSP headers allow maps.googleapis.com, maps.gstatic.com across site and admin.
File: apps/admin/app/components/AddressesTab.tsx:33 — dynamically loads Google Maps JS API.
The design-foundation.md allows "Third-party map tiles (Mapbox) are permitted" but names Mapbox specifically, not Google Maps.
Severity: Minor — amendment candidate if Google Maps is the chosen provider
M-10: No mobile graceful degradation in workspace studio¶
Constitution reference: routing-structure.md — "Below [1280px]: graceful degradation message"
Violation: No evidence of a graceful degradation message for the studio designer on mobile. The studio layout exists (apps/workspace/app/studio/layout.tsx) but no viewport check or degradation message found.
Severity: Minor — not yet implemented
M-11: Workspace domain is my.rtopacks.com.au per constitution but routes as custom_domain¶
The workspace wrangler routes to my.rtopacks.com.au/* which matches the constitution. Confirmed compliant.
Severity: Minor — informational
M-12: KN content accessible in Drawer without tier gating visible¶
Constitution reference: presentation-tiers.md — "If kn prop is absent, the component renders the absent state"
packages/ui/src/unit/UnitDrawer.tsx:289-296 checks for kn_criteria_reasoning and renders it if present. The data is fetched and passed from the page level (apps/site/app/api/detail/route.js:60 returns kn_criteria_reasoning for any authenticated/unauthenticated request). There is no tier-based gating of KN data at the API or rendering layer.
Severity: Minor — KN data is publicly accessible via the detail API
Route Map¶
Public Site — rtopacks.com.au (apps/site)¶
| Route | Constitution Status | Auth | Notes |
|---|---|---|---|
/ |
Compliant | No | Homepage |
/about |
Compliant | No | Marketing |
/plans |
Compliant | No | Pricing (constitution says /pricing) |
/contact |
Compliant | No | Contact |
/catalogue |
Not in constitution | No | Product catalogue |
/auth |
Maps to /login in constitution |
No | Magic link entry |
/auth/verify |
Not in constitution | No | Auth flow |
/claim |
Not in constitution | No | RTO claim flow |
/checkout-auth |
Not in constitution | No | Checkout auth |
/invite/[token] |
Not in constitution | No | Invite acceptance |
/qual/[code] |
VIOLATION — should be gated at launch | No | Public qual detail |
/unit/[code] |
VIOLATION — should be gated at launch | No | Public unit detail |
/account |
Not in constitution | Yes | Account page |
/account/orders |
Not in constitution | Yes | Order history |
/dashboard |
Not in constitution | Yes | Dashboard |
/dashboard/composer |
Not in constitution | Yes | Composer |
/dashboard/members |
Not in constitution | Yes | Team management |
/dashboard/pathway |
Not in constitution | Yes | Career pathway |
/dashboard/pricing |
Not in constitution | Yes | Pricing view |
/dashboard/scope |
Not in constitution | Yes | Scope view |
/dashboard/settings |
Not in constitution | Yes | Settings |
/dashboard/trainer-mapper |
Not in constitution | Yes | Trainer mapping |
Workspace Studio — my.rtopacks.com.au (apps/workspace)¶
| Route | Constitution Status | Auth | Notes |
|---|---|---|---|
/ |
Partial — constitution says AppGrid | Yes | AppGrid launcher |
/auth |
Not in routing doc | No | Magic link auth |
/studio |
Compliant | Yes | Studio page |
/documents |
Not in constitution | Yes | Document manager |
/documents/[id] |
Not in constitution | Yes | Document detail |
/people |
Not in constitution | Yes | People management |
/people/[id] |
Not in constitution | Yes | Person detail |
/gened-store |
Not in constitution | Yes | Gen-ed store |
/landscape |
Not in constitution | Yes | Landscape intel |
/lms-connector |
Not in constitution | Yes | LMS connector |
/market-data |
Not in constitution | Yes | Market data |
/marketer |
Not in constitution | Yes | Marketing tools |
/radar |
Not in constitution | Yes | Radar intel |
/sms-connect |
Not in constitution | Yes | SMS tools |
Missing from workspace: /library, /courses, /settings, /settings/team, /settings/activity, /settings/exports, /skill-set/[code], /rto/[code], /gen-ed/[code]
Admin Panel — admin.rtopacks.com.au (apps/admin)¶
| Route | Constitution Status | Auth | Notes |
|---|---|---|---|
/ |
Compliant | Yes (CF Access) | Dashboard |
/organisations |
Compliant | Yes | Org management |
/organisations/[code] |
Compliant | Yes | Org detail |
/contacts |
Compliant | Yes | Contact CRM |
/contacts/[id] |
Not in constitution | Yes | Contact detail |
/clients |
Compliant | Yes | Client accounts |
/clients/[orgId] |
Not in constitution | Yes | Client detail |
/clients/new |
Not in constitution | Yes | New client |
/corpus/units |
Not in constitution | Yes | Unit corpus browser |
/corpus/units/[code] |
Not in constitution | Yes | Unit detail |
/corpus/quals |
Not in constitution | Yes | Qual corpus browser |
/corpus/quals/[code] |
Not in constitution | Yes | Qual detail |
/corpus/kn |
Not in constitution | Yes | KN stats |
/landscape |
Compliant | Yes | Landscape module |
/landscape/[slug] |
Not in constitution | Yes | Vendor detail |
/analytics/site |
Not in constitution | Yes | Site analytics |
/analytics/visitors |
Not in constitution | Yes | Visitor analytics |
/analytics/corpus |
Not in constitution | Yes | Corpus analytics |
/analytics/usage |
Not in constitution | Yes | Usage analytics |
/compute |
Not in constitution | Yes | Compute stats |
/finance |
Not in constitution | Yes | Finance |
/marketing |
Not in constitution | Yes | Marketing |
/calendar |
Not in constitution | Yes | Calendar |
/interactions/unmatched |
Not in constitution | Yes | Unmatched interactions |
/settings/webhooks |
Maps to /settings in constitution |
Yes | Webhook config |
Missing from admin: /compliance, /radar (listed in constitution, exists in workspace instead), /interactions
Component Inventory¶
packages/ui/src/ (currently @rtopacks/ui, should be @rtopacks/training-ui)¶
| Component | Location | Tier Mapping | Constitution Tier | Compliant |
|---|---|---|---|---|
| QualHeader | src/qual/QualHeader.tsx |
Page sub-component | Part of QualPage | Partial |
| QualDetailTab | src/qual/QualDetailTab.tsx |
Page tab | Part of QualPage | Partial |
| QualMarketTab | src/qual/QualMarketTab.tsx |
Page tab | Part of QualPage | Partial |
| QualRTOsTab | src/qual/QualRTOsTab.tsx |
Page tab | Part of QualPage | Partial |
| QualUnitsTab | src/qual/QualUnitsTab.tsx |
Page tab | Part of QualPage | Partial |
| QualTabs | src/qual/QualTabs.tsx |
Page navigation | Part of QualPage | Partial |
| UnitDrawer | src/unit/UnitDrawer.tsx |
Drawer | UnitDrawer | Compliant name |
| UnitElements | src/unit/UnitElements.tsx |
Drawer sub-component | Part of UnitDrawer | Partial |
| SearchOverlay | src/search/SearchOverlay.tsx |
N/A | Not in tiers | N/A |
| VideoBackground | src/video/VideoBackground.tsx |
N/A | Not in tiers | N/A |
| VideoScrim | src/video/VideoScrim.tsx |
N/A | Not in tiers | N/A |
Missing components (per constitution): - All Chip components (UnitChip, QualChip, SkillSetChip, RTOChip, GenEdChip) - All Card components (UnitCard, QualCard, SkillSetCard, RTOCard, GenEdCard) - SkillSetDrawer, RTODrawer, GenEdDrawer - UnitPage, QualPage, SkillSetPage, RTOPage, GenEdPage - All print components (PrintWrapper, PrintFooter, PrintHeader, usePrint) - All General Education Unit components - All RTO components - All Skill Set components
Sync script (SHARED-01): Components are duplicated in apps/site/app/components/ (QualHeader, QualDetailTab, etc., UnitDrawer, etc.) alongside the package source. No sync script found — files appear to be maintained independently.
Database Usage Map¶
D1 Database Bindings Across All Surfaces¶
| Database | UUID | Constitution Status | Surfaces Bound |
|---|---|---|---|
| rtopacks-db | 334ac8fb | READ ONLY (violated) | site (rtopacks_db), admin (RTOPACKS_DB), workspace (DB), internal-api (RTOPACKS_DB), geocoder (DB), kn-preview (DB), api (DB), mcp (DB), prelaunch (RTOPACKS_DB), jsa-ingest (DB), ivi-ingest (DB), d1-warmer (DB), glmd-ingest (DB), osl-ingest (DB), vnda-atlas-ingest (DB), workspace-worker (DB) |
| ops-db | 00daba3d | Compliant | site (ops_db), admin (ops_db), workspace (OPS_DB), internal-api (OPS_DB), traffic-logger (DB), magda-monitor (DB), prelaunch (OPS_DB) |
| engine-db | fb6ddc43 | Compliant | site (engine_db), admin (ENGINE_DB) |
| microcredentials-db | 3924412d | Unused in code | site (micro_db) |
| licensing-db | 6c2abf4d | Compliant | site (licensing_db), admin (LICENSING_DB), teqsa-sync (DB), mcp (LICENSING_DB) |
| abs-db | 66389f3f | Compliant | admin (ABS_DB), d1-warmer (ABS_DB) |
| radar-db | d5e88e32 | Compliant | admin (RADAR_DB) |
| landscape-db | dd355937 | Compliant | admin (LANDSCAPE_DB) |
| calendar-db | c51c8778 | Not in constitution | site (calendar_db), admin (CALENDAR_DB), cal-sync (CAL_DB) |
Write Operations to rtopacks-db (should be zero)¶
See C-01 for full list. Summary: 30+ distinct write operations from apps/site, plus writes from geocoder worker and admin nav-prefs.
Workers Accessing External APIs Without docs/ops/ Reference¶
| Worker | External API | docs/ops/ Doc Exists? |
|---|---|---|
| geocoder | Google Geocoding API | No |
| teqsa-sync | TEQSA public API | No |
| jsa-ingest | Jobs and Skills Australia API | No |
| ivi-ingest | Internet Vacancy Index | No |
| magda-monitor | data.gov.au MAGDA | No |
| cal-sync | (Calendar source TBD) | No |
| mcp | MCP protocol | No |
Unresolved Items¶
U-01: Site app dual-purpose architecture¶
The apps/site app serves both the public marketing surface AND the authenticated dashboard (/dashboard/*). The constitution defines these as separate surfaces (public site vs Workspace Studio). The current architecture has a single app handling both, which creates the auth boundary ambiguity. Tim decision required: is this acceptable or does it need splitting?
U-02: Compliance mode export button¶
The admin app has a ComplianceExport component in apps/admin/app/components/mode-badge.tsx:87-91 that renders only in COMPLIANCE mode. The constitution (mode-system.md) confirms this is correct behaviour that should be formalised. Current implementation is minimal — it needs a full brief.
U-03: Dashboard routes on site vs workspace¶
The constitution places all authenticated RTO features at my.rtopacks.com.au (Workspace Studio). However, the current codebase has /dashboard/* routes on rtopacks.com.au (the public site app). These need to either migrate to the workspace app or the routing-structure.md needs amending.
U-04: Admin corpus browser¶
The admin panel has /corpus/units, /corpus/quals, and /corpus/kn routes that are not in the routing-structure.md. These provide corpus management views. Tim should decide if these are correct admin features that need documenting, or if they should be restructured.
U-05: Workspace apps not in constitution¶
The workspace has apps (gened-store, landscape, lms-connector, market-data, marketer, radar, sms-connect, people, documents) that are not documented in routing-structure.md. These appear to be features built to the AppGrid concept. They need either documenting or reviewing.
U-06: Developer API/MCP infrastructure¶
apps/site/app/lib/developer-keys.js and workers/api/, workers/mcp/ implement a developer API and MCP server infrastructure that is not referenced in any constitution document. This is a significant capability that needs documenting.
U-07: Prelaunch worker writes to ops-db (contacts)¶
workers/prelaunch/ manages contact signups and writes to ops-db. This appears correct for pre-launch lead capture but is not documented.
U-08: Multiple data ingest workers write to rtopacks-db¶
Workers jsa-ingest, ivi-ingest, glmd-ingest, osl-ingest, vnda-atlas-ingest write to rtopacks-db. These are data pipeline workers that populate NRT-adjacent data. The constitution says rtopacks-db is READ ONLY — Tim needs to decide if these are exempt as data pipeline operations or if the data should live elsewhere.
STALE-OPS-01: Stale tables remaining in ops-db after DB-SPLIT-01¶
The following tables were copied (not moved) from ops-db to workspace-db during DB-SPLIT-01. The source copies in ops-db are now stale and should be dropped once clean operation of workspace-db is confirmed:
people(10 rows)people_tae(2 rows)people_industry_currency(0 rows)documents(0 rows)document_versions(0 rows)document_categories(18 rows)
Action: Confirm workspace apps read/write correctly from workspace-db, then raise DROP-STALE-01 to delete these tables from ops-db.
Recommended Brief Order¶
| Priority | Brief ID | Description | Addresses |
|---|---|---|---|
| 1 | DB-SPLIT-01 | Migrate all non-NRT tables (users, magic_tokens, dockets, tenants, composer_sessions, developer_keys, partner_accounts, audit_log, traffic_log, calendar, contacts, etc.) from rtopacks-db to ops-db | C-01, S-10 |
| 2 | API-MIGRATE-01 | Route all NRT data access through internal-api.rtopacks.com.au — remove direct rtopacks-db access from all surfaces | C-02 |
| 3 | SEO-LOCK-01 | Remove sitemap.js, remove JSON-LD from public qual/unit pages, gate public data pages | C-03, S-01 |
| 4 | SEC-KEYS-01 | Remove all hardcoded API keys from source, move to Worker secrets | C-04 |
| 5 | HEADER-FIX-01 | Remove UCCA provenance headers from admin middleware | C-05 |
| 6 | SHARED-01 | Create @rtopacks/training-ui package with five-object tier structure, sync script | S-02 |
| 7 | MODE-01 | Implement mode system across all surfaces with KV persistence and ops-db logging | S-03 |
| 8 | CSP-NONCE-01 | Implement nonce-based CSP across all surfaces — requires custom middleware for OpenNext/Cloudflare Workers nonce injection | S-04 (remaining unsafe-inline) |
| 9 | ~~FOUNDATION-01~~ | ~~Audit and fix hex values, font weights, uppercase across all surfaces~~ DONE f694f8b5 |
~~S-05, S-06, S-07~~ |
| 10 | PRINT-01 | Build print primitive (PrintWrapper, PrintFooter, PrintHeader, usePrint) | S-08 |
| 11 | SEC-01 | Per-session attribution tokens, canary values, scraping defence | S-09 |
| 12 | OBJECT-01 | Implement Skill Set and General Education Unit across all layers | S-11 |
| 13 | MIGRATE-01 | Site/workspace auth split — two parallel auth systems (rtopacks_session + rtp_session on same KV), dashboard routes on site instead of workspace |
High — before real users |
| 14 | IDENTITY-01 | Real tier resolution — ucca_layer hardcoded at L3 for all workspace sessions in bootstrapSession(). No actual L4/L4A differentiation until identity resolution is built |
High — before real users |
| 15 | AUTH-PATTERN-01 | Site auth.js calls getCloudflareContext() inline in every function — should pass env bindings as params (workspace pattern). Prerequisite for API-GATE-01 |
Medium |
| 16 | ~~HEALTH-01~~ | ~~/_health and /_build endpoints missing on all three surfaces~~ DONE 2bc9b780 |
~~Easy win~~ |
| 17 | ADMIN-SESSION-01 | Admin has no session concept beyond CF Access headers — no session sliding, no revocation, no cross-request activity tracking | Low — future consideration |
Amendment Candidates¶
Items that represent correct current state but are not in the constitution:
| Item | Current State | Proposed Amendment |
|---|---|---|
| calendar-db | c51c8778 bound to site and admin |
Add to object-model.md database table |
| Developer API | Full API key management, MCP server | Document in security-posture.md or new developer-api.md |
| Corpus browser | Admin corpus management routes | Add to routing-structure.md admin section |
| Workspace apps | 10+ apps on AppGrid | Add to routing-structure.md workspace section |
| Google Maps | Used instead of Mapbox | Update design-foundation.md EXT-ASSET exceptions |
| Dashboard routes | Currently on site, not workspace | Decide: migrate or amend routing-structure.md |
/plans vs /pricing |
Route is /plans, constitution says /pricing |
Amend routing-structure.md or rename route |
/auth vs /login |
Route is /auth, constitution says /login |
Amend routing-structure.md or rename route |
| Data ingest workers | Write to rtopacks-db as data pipeline | Add data pipeline exception to object-model.md if intentional |
End of AUDIT-01 report. No changes were made to the codebase during this audit.