Signals Vault – Auth

Auth system complete. Two auth paths, both project-scoped:

Path A — AccidAuth (primary) routers/auth.py

  • POST /auth/verify-token — accepts X-Accid-Key + X-Accid-Hash + X-ACCID-App headers
  • Auto-registers on first call (no separate registration step)
  • Project scoping: pass {"project_id": "dragon-lady"} in body — keys accumulate project access on each successful auth
  • GET /auth/key-info — returns registered projects, app, timestamps
  • POST /auth/revoke-project — removes a project from a key's access list
  • validate_accid_auth(request, project_id) — helper for other routers
  • Storage: data/_auth/tokens.json

Path B — Edit Tokens (secondary) — unchanged in save_pipeline.py

  • POST /api/project/{id}/generate-edit-token — shareable per-project tokens
  • GET /api/project/{id}/check-edit-access — validates token or localhost bypass

Wiring:

  • vault_ultra.py — auth router mounted with tags=["auth"]
  • save_pipeline.py — _validate_auth_headers() now delegates to real validate_accid_auth(), passes project scope. Fixed
    the except Exception swallowing HTTPException bug (now re-raises 401 properly instead of 500).

Client-side accid-auth.js:

  • Added projectId to constructor (from options, data-accid-project attribute, , or
    ?project= URL param)

  • validateWithVault() now sends { project_id } in the POST body

  • saveCredentials() includes projectId in persisted data

    What We Built

    vault_ultra is now a fully working ACCID Signal Hub v3.0, running on port 9849. We took the flat reference files from
    tmp/ultra/ and turned them into a proper FastAPI app with 7 routers and 42+ endpoints.

    Session 1 was about getting the foundation right:

    • Organized files into vault_ultra/ with a routers/ subdirectory
    • Fixed 3 bugs: import re at bottom of config, ssh –version flag wrong, route collision on /api/projects/trashed
    • Created local start.sh and fresh venv (the old one was broken)
    • Ran forensic E2E tests — all passed
    • Compared against ACCID_SOURCE and rewrote the trash system to metadata-only (blotter flags, files never move)
    • Added a self-rebuilding project registry
    • Added per-project edit tokens (Path B)

    Session 2 was the auth system:

    • Built routers/auth.py — the AccidAuth protocol (Path A) with project scoping
    • Auto-registration on first call, project lists per key, revoke support
    • Wired save_pipeline.py to use real auth (caught a bug where except Exception was swallowing 401s into 500s)
    • Updated accid-auth.js client-side with projectId support (data attributes, meta tags, URL params)
    • All 12 E2E tests pass

      {
        "document": "vault_ultra_build_summary",
        "version": "3.0",
        "date": "2026-02-11",
        "server": {
          "port": 9849,
          "framework": "FastAPI",
          "python": "3.14",
          "working_dir": "/Volumes/SpiffyMagic/ACCID_MANAGEMENT/ACCID_VAULT_local/vault_ultra",
          "venv": "vault_ultra/venv",
          "start_script": "vault_ultra/start.sh"
        },
        "architecture": {
          "entry_point": "vault_ultra.py",
          "config": "config.py",
          "routers_dir": "routers/",
          "data_dir": "data/",
          "routers": [
            "projects.py",
            "save_pipeline.py",
            "deploy.py",
            "data_import.py",
            "search.py",
            "images.py",
            "auth.py"
          ]
        },
        "auth_system": {
          "path_a_primary": {
            "name": "AccidAuth",
            "endpoint": "POST /auth/verify-token",
            "headers": ["X-Accid-Key", "X-Accid-Hash", "X-ACCID-App"],
            "body_optional": { "project_id": "string" },
            "hash_formula": "SHA256(key + ':' + passcode)",
            "behavior": "auto-registers on first call, validates on subsequent",
            "project_scoping": "keys accumulate project access on each successful auth",
            "storage": "data/_auth/tokens.json",
            "additional_endpoints": [
              "GET /auth/key-info",
              "POST /auth/revoke-project"
            ],
            "helper_function": "validate_accid_auth(request, project_id)"
          },
          "path_b_secondary": {
            "name": "Edit Tokens",
            "generate": "POST /api/project/{id}/generate-edit-token",
            "check": "GET /api/project/{id}/check-edit-access",
            "hash_formula": "SHA256(token + passphrase + project_id)",
            "storage": "per-project accid_blotter.json edit_config field",
            "localhost_bypass": true
          },
          "backward_compat": "no auth headers = skip validation (migration period)"
        },
        "client_side": {
          "file": "htmlbuilder_local/js/accid-auth.js",
          "class": "AccidAuth",
          "project_scope_sources": [
            "options.projectId",
            "data-accid-project attribute",
            "meta[name=accid-project]",
            "?project= URL param"
          ],
          "vault_integration": "POST ${vaultUrl}/auth/verify-token with project_id in body"
        },
        "trash_system": {
          "pattern": "metadata-only (matches ACCID_SOURCE)",
          "blotter_fields": ["is_trashed", "trash-status", "trash-history"],
          "behavior": "files never move, blotter flags control visibility",
          "safety_guard": "permanent delete requires trashed state first",
          "endpoints": [
            "DELETE /api/project/{id}/remove (soft delete)",
            "POST /api/project/{id}/restore",
            "DELETE /api/project/{id}/permanent (hard delete)"
          ]
        },
        "registry": {
          "file": "data/project_registry.json",
          "self_rebuilding": true,
          "rebuild_trigger": "auto on missing/corrupt, manual via POST /api/registry/rebuild",
          "scans": "all project dirs, reads blotters, computes stats"
        },
        "bugs_fixed": [
          "import re at bottom of config.py → moved to top",
          "ssh --version → ssh -V (stderr output)",
          "ssh-keygen --version → ssh-keygen -h (non-zero exit OK)",
          "/api/projects/trashed route collision with /api/projects/{project_type} → reordered",
          "except Exception swallowing HTTPException (401→500) in save_pipeline → added except HTTPException: raise"
        ],
        "endpoint_groups": {
          "projects": "18 routes - CRUD, onboarding, trash, backup, registry",
          "save_pipeline": "7 routes - htmlbuilder/galleries/tagger saves + edit tokens",
          "deploy": "4 routes - FTP save, test, per-project credentials",
          "data_import": "4 routes - tagger import pipeline + 3 stubs (501)",
          "search": "3 routes - config get/update, rebuild index",
          "images": "3 routes - proxy with cache, collect for deploy, URL rewrite",
          "auth": "3 routes - verify-token, key-info, revoke-project",
          "health": "3 routes - root page, /health, /api/system/validate"
        },
        "remaining_stubs": [
          "POST /api/tagger/upload-imgbb (501)",
          "POST /api/tagger/upload-local (501)",
          "GET /api/tagger/images (501)"
        ],
        "key_files_modified": [
          "vault_ultra/vault_ultra.py",
          "vault_ultra/config.py",
          "vault_ultra/routers/projects.py",
          "vault_ultra/routers/save_pipeline.py",
          "vault_ultra/routers/auth.py (NEW)",
          "htmlbuilder_local/js/accid-auth.js"
        ]
      }