{
  "id": "cms",
  "type": "module",
  "version": "0.5.0-track5",
  "label": "Oscar Tree Academy CMS",
  "description": "Single-admin CMS for Oscar Tree Academy staging. v0.5.0-track5 hardened. Phase 4 deploy.",
  "constitutional_fragments": [
    "FWV v8 \u00a706 (file < 200 lines)",
    "FWV v8 \u00a710 (module as cluster)",
    "FWV v8 \u00a735 (module-pact-registration)",
    "FVW Section C (CMS Core, Stage 2)"
  ],
  "entry": "index.js",
  "prefix": "oscar-tree",
  "files": [
    "module.json",
    "index.js",
    "cms-doctrine.md",
    "schema.js",
    "schema-id.js",
    "validator.js",
    "validator-types.js",
    "views-helpers.js",
    "wordlist.js",
    "storage/adapter.js",
    "storage/local-storage-adapter.js",
    "schemas/index.js",
    "schemas/siteSettings.js",
    "schemas/book.js",
    "store.js",
    "store-bulk.js",
    "activity.js",
    "backups.js",
    "backups-keys.js",
    "backups-rotate.js",
    "search.js",
    "ai-prompts.js",
    "auth/auth.js",
    "auth/auth-crypto.js",
    "auth/auth-state.js",
    "auth/auth-dev.js",
    "auth/auth-routes.js",
    "auth/SECURITY.md",
    "router.js",
    "router-routes-r.js",
    "router-routes-s.js",
    "router-routes-t.js",
    "mounts.js",
    "ui.js",
    "ui-array.js",
    "ui-renderers.js",
    "form-bindings.js",
    "stubs.js",
    "stub-store.js",
    "views-helpers.js",
    "views-dashboard.js",
    "views-activity.js",
    "views-backups.js",
    "views-help.js",
    "views-search.js",
    "editors/_editor-helpers.js",
    "editors/_editor-delete.js",
    "editors/siteSettings.js",
    "upload/json.js",
    "upload/jsonl.js",
    "upload/csv.js",
    "upload/ai.js",
    "upload/upload-page.js",
    "upload/_upload-helpers.js",
    "admin.html",
    "admin.css",
    "admin.js",
    "README.md",
    "USAGE.md",
    "SECURITY.md",
    "MIGRATION.md",
    "SCHEMAS.md",
    "track-0-report.md",
    "track-0-test-results.md",
    "docs/FAQ.md",
    "docs/TROUBLESHOOTING.md",
    "docs/A11Y.md",
    "docs/PRIVACY-AND-I18N.md",
    "tests/test-security.js",
    "tests/test-security-source.js",
    "tests/test-activity-perf.js",
    "tests/test-backup-size.js",
    "track-5-report.md"
  ],
  "tests": {
    "track_0_to_3": "tests/test-{store,cascade,activity,backups,backups-rotate,schema,validator,storage,auth,auth-extras,router,ui,form-rendering}.js",
    "track_4_acceptance": "tests/acceptance/site-smoke.test.js",
    "track_4_adoption": "tests/test-book-schema.js",
    "track_5_security": "tests/test-security.js",
    "track_5_security_source": "tests/test-security-source.js",
    "track_5_perf": "tests/test-activity-perf.js",
    "track_5_backup_size": "tests/test-backup-size.js",
    "fixtures": "tests/fixtures/sample-site.js",
    "total": 189
  },
  "scenarios": [
    "S1: setup creates admin user and returns 12 recovery words",
    "S2: login wrong password fails and increments failure counter",
    "S3: login correct password succeeds and sets session",
    "S4: cmsUpsert creates, cmsList retrieves, cmsGet finds, cmsUpdate modifies",
    "S5: validation rejects invalid id, missing required, unknown field",
    "S6: cascade delete warns when references exist; force cascades through",
    "S7: backup round-trip restores records; auth is never in the envelope",
    "S8: activity log captures 5 CRUD ops newest-first with correct summary",
    "S9: auto-backup rotation across 3 calls; latest envelope present at slot 0",
    "S10: global search across 3 schemas finds expected matches"
  ],
  "storage_keys": [
    "{prefix}:admin:auth",
    "{prefix}:admin:activity",
    "{prefix}:admin:backups",
    "{prefix}:admin:auto-backup-{0..3}",
    "{prefix}:admin:{schemaId}"
  ],
  "auth": {
    "algorithm": "PBKDF2-SHA256",
    "iterations": 310000,
    "min_password_length": 12,
    "recovery_words": 12,
    "lockout_threshold": 5,
    "lockout_duration_ms": 300000,
    "session_duration_ms": 604800000,
    "session_storage": "sessionStorage",
    "dev_mode_storage_key": "{prefix}:admin:devMode"
  },
  "depends_on": [],
  "consumed_by": [
    "shell/admin.html (Track 3)"
  ],
  "storage_adapter": "local",
  "routing": "hash",
  "pressure_test_fixes_baked_in": [
    "A.L1 \u2014 fv-content-id is full SHA-1 (40 hex chars)",
    "A.L2 \u2014 fv-module confidence scoring (0..1)",
    "A.L4 \u2014 stable identity across rebuilds via identityKey"
  ],
  "track": 5,
  "status": "hardened",
  "track_5_fixes": [
    "SEC11: sanitizeHtml blocks <script>, event handlers, javascript: URLs, <style>, <iframe>",
    "SEC12: validateUrl blocks localhost, .local, private IPv4 ranges, link-local (gap documented for IPv6 \u2014 TRACK-6-SSRF-V6)",
    "SEC13: source audit \u2014 no postMessage / contentWindow / .frames anywhere in cms/",
    "SEC14: backups.js source audit \u2014 auth key only referenced as a rejection constant, never read/written",
    "SEC15: source audit \u2014 every innerHTML write in views/ + editors/ + upload/ goes through escapeHtml / sanitizeHtml / pre-escaped renderXxx",
    "SEC16: CRYPTO_CONFIG is frozen (Object.isFrozen === true), PBKDF2-SHA256 / 310000 iters",
    "SEC17: constantTimeEqual null / undefined / non-string returns false",
    "PERF1-4: activity log perf smoke \u2014 1000 inserts < 5s; reads < 100ms; bulk read 1000x < 1s",
    "BSZ1-4: backup envelope \u2014 1000 records < 5MB; round-trip lossless; parse < 500ms; preview diff accurate",
    "A11Y: posture documented in docs/A11Y.md (mostly manual; Track 6 for auto-enforcement)",
    "GDPR: posture documented in docs/PRIVACY-AND-I18N.md + SECURITY.md privacy section",
    "i18n: limitation documented \u2014 strings are English-only in v1.0"
  ],
  "track_6_follow_ups": [
    "TRACK-6-SSRF-V6: validateUrl hostname normalization for IPv6 (e.g. [::1], fc00::/7)",
    "TRACK-6-A11Y-ARIA: programmatic aria-invalid + aria-describedby on form errors",
    "TRACK-6-A11Y-FOCUSTRAP: focus trap on delete + import-preview modals",
    "TRACK-6-A11Y-LIVE: aria-live=\"polite\" on status regions",
    "TRACK-6-A11Y-TABLE: <th scope> headers in activity table",
    "TRACK-6-RETENTION: configurable retention (currently hardcoded 5000)",
    "TRACK-6-PBKDF2-ADAPT: optional adaptive iterations for slow devices",
    "TRACK-6-CSP: migrate inline onclick to addEventListener for stricter CSP"
  ],
  "docs": {
    "README.md": "Quick-start + install + file layout",
    "USAGE.md": "How to add content types, use editors, bulk import, backups",
    "SECURITY.md": "Threat model + checklist + privacy posture (operator overview)",
    "MIGRATION.md": "Migrating from another CMS",
    "SCHEMAS.md": "Built-in + example schemas catalog",
    "docs/FAQ.md": "Common operator questions (10 entries)",
    "docs/TROUBLESHOOTING.md": "Common errors + fixes (10 entries)",
    "docs/A11Y.md": "Accessibility posture + manual checklist",
    "docs/PRIVACY-AND-I18N.md": "GDPR posture, retention policy, i18n limitation"
  }
}