diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..baf9cec --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @chittyos/assets diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e811add --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,191 @@ +# AGENTS.md — ChittyAssets + +Agent registry and working instructions for AI agents operating on this repository. + +**Service**: `chittycanon://core/services/chittyassets` +**Repo**: `CHITTYOS/chittyassets` +**Tier**: 4 (Domain — asset ownership verification, evidence pipeline, blockchain integration) +**Stack**: React 18 + Express 4 + Drizzle ORM + Neon PostgreSQL + GCS + ChittyChain + GPT-4o + ChittyAuth + +Read the Compliance Triad before making substantive changes: +- `CHARTER.md` — API contract, scope boundaries, dependency table, compliance flags +- `CHITTY.md` — Architecture, module map, asset lifecycle state machine, certification level +- `CLAUDE.md` — Dev commands, Drizzle patterns, integration gotchas + +--- + +## Common Commands + +```bash +npm run dev # Vite frontend + Express backend with hot reload +npm run build # Production build (Vite frontend + server bundle) +npm start # Run production server +npm run db:push # Push Drizzle schema changes to Neon (steep-cloud-28172078) +npm run check # TypeScript type checking (client + server) +``` + +Secrets are managed via 1Password — never hardcode: +```bash +op run --env-file=.env.chittyos -- npm run dev +``` + +Deploy to Cloudflare: +```bash +npx wrangler deploy --env production +curl -s https://chittyassets-api-prod.workers.dev/health | jq . +``` + +--- + +## Architecture Quick Reference + +``` +Browser / Mobile + React SPA (Vite, Wouter, TanStack Query, Radix UI) + | + Express API (Node.js 20) + | + ┌──────────────────────────────────────────────┐ + │ chittyAuth.ts → auth.chitty.cc │ + │ chittyCore.ts → id.chitty.cc │ + │ → trust.chitty.cc │ + │ → chain.chitty.cc │ + │ → ledger.chitty.cc │ + │ aiAnalysis.ts → OpenAI GPT-4o │ + │ objectStorage.ts → Google Cloud Storage │ + │ storage.ts → Neon PostgreSQL │ + └──────────────────────────────────────────────┘ + | + Cloudflare Worker (chittyassets-api-prod) +``` + +**Key source files:** + +| File | Responsibility | +|------|---------------| +| `server/routes.ts` | All API endpoints, middleware binding | +| `server/chittyAuth.ts` | JWT validation against auth.chitty.cc | +| `server/chittyCore.ts` | Unified client for ChittyOS upstream services | +| `server/aiAnalysis.ts` | GPT-4o analysis (receipt, document, valuation, legal) | +| `server/objectStorage.ts` | GCS signed URLs and ACL policy enforcement | +| `server/storage.ts` | Drizzle ORM abstraction for all DB operations | +| `shared/schema.ts` | Zod + Drizzle schemas shared by client and server | +| `server/githubWebhooks.ts` | GitHub App event handling | +| `server/toolRegistry.ts` | Agent-accessible connector surface | + +--- + +## Asset Lifecycle State Machine + +``` +DRAFT → (freeze) → FROZEN → (7 days) → (mint) → MINTED + | | + | Evidence/AI analysis window + | + (delete) → removed +``` + +State transitions are gated in `server/routes.ts`. All transitions write immutable `timelineEvents` records. Agents must not skip state machine steps — no direct DB writes that bypass the route layer. + +--- + +## Database Schema (Neon — `steep-cloud-28172078`) + +Core tables (Drizzle ORM, `shared/schema.ts`): + +- `users` — ChittyID-anchored user records +- `assets` — Asset registry: `chittyId`, `trustScore`, `chittyChainStatus` (draft/frozen/minted), `ipfsHash`, `blockchainHash` +- `evidence` — Evidence files linked to assets: `verificationStatus`, `aiAnalysis` +- `aiAnalysisResults` — GPT-4o output: `analysisType`, `confidence`, `results`, `processingTime`, `modelUsed` +- `timelineEvents` — Immutable append-only event log per asset +- `warranties` — Warranty policies per asset +- `insurancePolicies` — Insurance records per asset +- `legalCases` — Legal case records per user + +Always run `npm run db:push` after schema changes. Validate the SQL against the real Neon project before committing — no placeholder migrations. + +--- + +## Integration Patterns + +### ChittyAuth JWT + +All protected routes use `requireChittyAuth()` middleware from `server/chittyAuth.ts`. The middleware validates JWTs issued by `auth.chitty.cc`. Do not add custom auth bypasses or fallback token logic — route through ChittyAuth. + +### ChittyOS Core Services (chittyCore.ts) + +Calls to `id.chitty.cc`, `trust.chitty.cc`, `chain.chitty.cc`, and `ledger.chitty.cc` are centralized in `server/chittyCore.ts`. Add new upstream calls here — do not scatter direct `fetch()` calls to ChittyOS services across route handlers. + +### GPT-4o Evidence Analysis + +`server/aiAnalysis.ts` accepts an enumerated `analysisType` (`receipt` | `document` | `asset_valuation`). Results are stored as structured JSON in `aiAnalysisResults`. Do not change the schema of stored results without updating the Drizzle schema and re-validating existing records. + +### GCS File Access + +All evidence file access flows through `server/objectStorage.ts`. The ACL check (`ObjectPermission.READ`) must run before any GCS stream is returned. Never add a route that proxies GCS objects without going through this module. + +### Evidence Ledger + +Submissions to `ledger.chitty.cc` (`POST /api/evidence-ledger/submit`) must include the evidence ChittyID. Read the `chittycanon://core/services/evidence` CHARTER.md before extending this integration. + +--- + +## Coding Standards + +- Language: TypeScript (strict mode). Indentation: 2 spaces. +- Filenames: `camelCase.ts` for server modules; `PascalCase.tsx` for React components. +- Schemas: Define in `shared/schema.ts` (Zod + Drizzle). Client and server share these — changes affect both. +- No mock data, fake data, or placeholder endpoints — see global CLAUDE.md policy. +- No `vi.mock()` / `jest.mock()` on DB or service modules in new tests. Tests hit real Neon branches. +- Every new route must have a corresponding real query against Neon before commit. + +--- + +## Testing + +Framework: Vitest (`vitest.config.ts`). Tests live under `test/`. + +```bash +npm test # Run full test suite +``` + +For Neon-backed tests: use the `steep-cloud-28172078` project on a dev branch. Never mock the DB layer in new test files. + +--- + +## Security Rules for Agents + +1. Do not commit secrets, API keys, or DB connection strings. All secrets via 1Password (`op run`). +2. Do not add a GCS streaming route without ACL enforcement via `objectStorage.ts`. +3. Do not modify `timelineEvents` records — this table is append-only. +4. Do not bypass `requireChittyAuth()` middleware on any route. +5. Do not send raw user input to GPT-4o without the enumerated `analysisType` gating in `aiAnalysis.ts`. +6. Evidence files have a 7-year retention minimum (FRE compliance) — do not add delete paths for evidence. +7. Before extending the evidence ledger integration, read the CHARTER.md of `chittycanon://core/services/evidence`. + +--- + +## Compliance Flags (Open — Do Not Fix Inline) + +These items require coordinated remediation — flag them in PRs but do not patch inline without a tracked issue: + +1. `wrangler.toml` `compatibility_date = "2024-01-01"` — update to `2025-01-01` minimum +2. Missing `[[hyperdrive]]` binding — Neon needs Cloudflare Hyperdrive for Worker deployments +3. Missing `[[tail_consumers]]` — ChittyChronicle and ChittyTrack audit streaming not wired +4. Replit Auth references in `CLAUDE.md` — confirm full replacement by ChittyAuth before Silver certification +5. `/health` and `/api/v1/status` endpoints — not yet present in `routes.ts`; mandatory before ChittyRegister submission + +--- + +## PR Requirements + +- Every PR must pass `npm run check` (TypeScript) and `npm test`. +- Routes touching the evidence or blockchain pipeline require a test against the real Neon dev branch. +- PRs that extend ChittyOS upstream integrations must reference the upstream service's CHARTER.md in the PR description. +- Do not merge PRs with open compliance flags from the list above without a linked tracking issue. + +--- + +## Capability Registration Note + +Do not add MCP servers, tools, or skills to local client configuration. New capabilities for this service route through Ch1tty's backend per the global CLAUDE.md capability registration policy. See `server/toolRegistry.ts` for the agent-accessible connector surface. diff --git a/CHARTER.md b/CHARTER.md new file mode 100644 index 0000000..30e34e2 --- /dev/null +++ b/CHARTER.md @@ -0,0 +1,225 @@ +--- +uri: chittycanon://docs/ops/policy/chittyassets-charter +namespace: chittycanon://docs/ops +type: policy +version: 1.0.0 +status: PENDING +registered_with: null +title: "ChittyAssets Charter" +certifier: chittycanon://core/services/chittycertify +visibility: PUBLIC +service_uri: chittycanon://core/services/chittyassets +--- + +# ChittyAssets — CHARTER + +## Classification + +- **Canonical URI**: `chittycanon://core/services/chittyassets` +- **Tier**: 4 (Domain — Business Logic) +- **Organization**: CHITTYOS +- **Primary URL**: assets.chitty.cc (target) / chittyassets-api-prod (Cloudflare Worker) +- **Repo**: `CHITTYOS/chittyassets` +- **Status**: MIGRATING (Express → Hono on Cloudflare Workers) +- **Entity types handled**: P (Person — users), L (Location — jurisdictions referenced on legal cases), T (Thing — assets, evidence files, warranty contracts, insurance policies), E (Event — timeline events, freeze, mint, legal proceedings), A (Authority — referenced credential issuers, courts, certifying authorities) + +## Mission + +Provide a blockchain-anchored asset ownership registry and evidence ledger for the ChittyOS ecosystem. ChittyAssets registers physical and digital assets, attaches AI-verified evidence, enforces a 7-day immutability freeze before blockchain finalization, and exposes ownership proofs consumable by downstream legal and insurance services. + +--- + +## Scope + +### This Service IS Responsible For + +- Asset CRUD — create, read, update, delete assets scoped to a ChittyID-authenticated user +- Evidence attachment — file upload (Google Cloud Storage), ACL enforcement, and evidence lifecycle per asset +- AI analysis — GPT-4o document analysis: receipt parsing, asset valuation, trust score calculation +- Blockchain integration — ChittyChain freeze (7-day) and mint (evidence token) workflows +- Evidence ledger — submitting, retrieving, and verifying evidence via `chittycanon://core/services/evidence` (ledger.chitty.cc) +- Legal document generation — AI-drafted ownership certificates, chain of custody reports +- Warranty and insurance tracking — expiry alerting, per-asset policy management +- Timeline event recording — immutable audit trail of all asset lifecycle events +- Ecosystem health surface — `/api/ecosystem/status` aggregating upstream service states + +### This Service IS NOT Responsible For + +- ChittyID minting (→ `chittycanon://core/services/identity`) +- Certificate signing (→ `chittycanon://core/services/cert`) +- User authentication token issuance (→ `chittycanon://core/services/auth`) +- Canonical URI assignment (→ `chittycanon://core/services/canon`) +- Schema governance (→ `chittycanon://core/services/schema`) +- Audit log immutability (→ `chittycanon://core/services/chronicle`) +- Service mesh binding (→ `chittycanon://core/services/discovery`) +- Trust score algorithm governance (→ `trust.chitty.cc`) +- Semantic memory persistence (→ `chittycanon://core/services/connect`) + +--- + +## Dependencies + +| Direction | Service | Canonical URI | Purpose | +|-----------|---------|---------------|---------| +| Upstream | ChittyAuth | `chittycanon://core/services/auth` | JWT authentication via auth.chitty.cc | +| Upstream | ChittyID | `chittycanon://core/services/identity` | ChittyID generation for assets (id.chitty.cc) | +| Upstream | ChittyTrust | `chittycanon://core/services/trust` | Trust score calculation (trust.chitty.cc) | +| Upstream | ChittySchema | `chittycanon://core/services/schema` | Schema validation for evidence payloads (schema.chitty.cc) | +| Upstream | ChittyChain | `chittycanon://core/services/chain` | Blockchain freeze and mint (chain.chitty.cc) | +| Upstream | ChittyEvidence | `chittycanon://core/services/evidence` | Evidence ledger submit/verify (ledger.chitty.cc) | +| Upstream | Neon PostgreSQL | (Neon project steep-cloud-28172078) | Structured data: assets, evidence, timeline, warranties | +| Upstream | Google Cloud Storage | (external) | Binary evidence file storage | +| Upstream | OpenAI GPT-4o | (external) | AI document analysis and valuation | +| Downstream | ChittyChronicle | `chittycanon://core/services/chronicle` | Audit event logging (non-blocking) | +| Downstream | ChittyDiscovery | `chittycanon://core/services/discovery` | Service mesh registration (non-blocking) | +| Peer | ChittyScore | `chittyscore` | Evidence weight and reputation scoring | + +--- + +## API Contract + +All routes except `/health` and `/api/v1/status` require a valid ChittyAuth JWT (`Authorization: Bearer ` or session cookie set by `auth.chitty.cc`). + +### Compliance Endpoints (Required) + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/health` | None | Health check — returns `{"status":"ok","service":"chittyassets"}` | +| GET | `/api/v1/status` | None | Service status and version | + +### Auth Routes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/auth/user` | JWT | Fetch authenticated user record | + +### Asset Routes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/assets` | JWT | List user assets with filters (`type`, `status`, `search`, `minValue`, `maxValue`) | +| POST | `/api/assets` | JWT | Create asset — generates ChittyID, calculates initial trust score | +| GET | `/api/assets/stats` | JWT | Aggregate portfolio statistics | +| GET | `/api/assets/:id` | JWT | Fetch single asset | +| PUT | `/api/assets/:id` | JWT | Update asset fields | +| DELETE | `/api/assets/:id` | JWT | Delete asset | +| POST | `/api/assets/:id/freeze` | JWT | Initiate 7-day ChittyChain immutability freeze | +| POST | `/api/assets/:id/mint` | JWT | Mint evidence token (requires frozen status) | +| POST | `/api/assets/:assetId/calculate-trust-score` | JWT | Recalculate trust score via AI analysis | + +### Evidence Routes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/assets/:assetId/evidence` | JWT | List evidence for asset | +| POST | `/api/assets/:assetId/evidence` | JWT | Attach evidence record | +| POST | `/api/evidence/:evidenceId/analyze` | JWT | GPT-4o analysis (`receipt`, `document`, `asset_valuation`) | + +### Evidence Ledger Routes (ChittyOS Ecosystem) + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| POST | `/api/evidence-ledger/submit` | JWT | Submit evidence to ChittyOS ledger at ledger.chitty.cc | +| GET | `/api/evidence-ledger/:chittyId` | JWT | Retrieve evidence by ChittyID from ledger | +| POST | `/api/evidence-ledger/:chittyId/verify` | JWT | Verify evidence authenticity | + +### Warranty Routes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/assets/:assetId/warranties` | JWT | List warranties for asset | +| POST | `/api/assets/:assetId/warranties` | JWT | Create warranty record | +| GET | `/api/warranties/expiring` | JWT | Expiring warranties (query: `days`, default 30) | + +### Insurance Routes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/assets/:assetId/insurance` | JWT | List insurance policies for asset | +| POST | `/api/assets/:assetId/insurance` | JWT | Attach insurance policy | + +### Legal Routes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/legal-cases` | JWT | List user legal cases | +| POST | `/api/legal-cases` | JWT | Create legal case | +| POST | `/api/legal/generate-document` | JWT | AI-generate legal ownership document | + +### Timeline Routes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/assets/:assetId/timeline` | JWT | Asset lifecycle event timeline | + +### Object Storage Routes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/objects/:objectPath` | JWT | Stream evidence file (ACL-enforced) | +| POST | `/api/objects/upload` | JWT | Get signed GCS upload URL | +| PUT | `/api/evidence-files` | JWT | Set ACL policy for uploaded evidence file | + +### Ecosystem Routes + +| Method | Path | Auth | Description | +|--------|------|------|-------------| +| GET | `/api/ecosystem/status` | JWT | Aggregated health of all ChittyOS upstream services | +| GET | `/api/tools/resources` | JWT | List tool/connector resources available to agent layer | +| POST | `/api/seed-demo` | JWT | Seed demo assets for onboarded user | + +--- + +## Schema Summary + +Core entities (Neon PostgreSQL, Drizzle ORM): + +- `users` — ChittyID-anchored user records +- `assets` — Asset registry with `chittyId`, `trustScore`, `chittyChainStatus` (`draft` | `frozen` | `minted`), `ipfsHash`, `blockchainHash` +- `evidence` — Evidence files linked to assets; includes `verificationStatus`, `aiAnalysis` +- `aiAnalysisResults` — GPT-4o output records: `analysisType`, `confidence`, `results`, `processingTime`, `modelUsed` +- `timelineEvents` — Immutable event log per asset +- `warranties` — Warranty policies linked to assets +- `insurancePolicies` — Insurance records linked to assets +- `legalCases` — Legal case records linked to users + +--- + +## Security + +- **Authentication**: JWT via ChittyAuth (`auth.chitty.cc`). Middleware: `requireChittyAuth()` on all protected routes. +- **Encryption**: TLS in transit (Cloudflare termination); AES-256-GCM for evidence data at rest (per `.env.chittyos` policy). +- **File ACL**: Evidence files private by default; per-object ACL enforced via `ObjectPermission.READ` check before any stream. +- **Evidence retention**: 7-year retention (2555 days) for legal compliance (FRE / USA Federal). +- **Chain of custody**: Strict — all state transitions recorded as immutable timeline events. + +--- + +## Compliance Flags (Known Issues — Not Yet Fixed) + +The following items are flagged for remediation prior to production certification. Do not fix inline — treat as a follow-up checklist: + +1. **Stale `wrangler.toml` compat date**: `compatibility_date = "2024-01-01"` should be `2025-01-01` (or current). Stale compat dates suppress runtime flag updates and can expose Workers to deprecated API behavior. +2. **Missing Hyperdrive binding**: Neon PostgreSQL is accessed via `@neondatabase/serverless` directly. For production Worker deployments, add a `[[hyperdrive]]` binding in `wrangler.toml` to use Cloudflare's connection pooling. Connection pooling over direct TCP from a Worker is unreliable under load. +3. **Missing `tail_consumers`**: No `[[tail_consumers]]` block in `wrangler.toml`. ChittyChronicle and ChittyTrack observability rely on tail consumers for real-time audit streaming. Add `[[tail_consumers]]` binding pointing to `chittychronicle` and `chittytrack` Workers. +4. **Auth migration incomplete**: `CLAUDE.md` references Replit Auth (Passport.js / OpenID Connect). Code uses ChittyAuth (`requireChittyAuth()`). Confirm Replit Auth is fully replaced before certifying. +5. **Health and status endpoints**: `/health` and `/api/v1/status` routes are not yet visible in `routes.ts`. These are mandatory for registration. They must be added before the POST to ChittyRegister. + +--- + +## Ownership + +- **Maintainer**: @chittyos/assets +- **Escalation**: assets@chitty.cc +- **Repo**: `https://github.com/chittyos/chittyassets` + +--- + +## Related Files + +| File | Purpose | +|------|---------| +| `CHARTER.md` | What this service IS — governance, scope, API contract | +| `CHITTY.md` | Where this service sits — architecture, ecosystem position | +| `CLAUDE.md` | How to WORK with this service — dev commands, patterns | +| `register.json` | Registration payload for ChittyRegister | diff --git a/CHITTY.md b/CHITTY.md new file mode 100644 index 0000000..2137144 --- /dev/null +++ b/CHITTY.md @@ -0,0 +1,176 @@ +--- +uri: chittycanon://core/services/chittyassets +tier: 4 +certification: BRONZE +status: PENDING +migration_status: MIGRATING_EXPRESS_TO_HONO +organization: CHITTYOS +--- + +# ChittyAssets — CHITTY + +## Ecosystem Position + +``` +LAYER 4 — DOMAIN (Business Logic) + +chittycanon://core/services/chittyassets +``` + +ChittyAssets is a Tier 4 Domain service. It consumes identity (ChittyID), trust (ChittyTrust), authentication (ChittyAuth), schema validation (ChittySchema), and blockchain anchoring (ChittyChain) from lower tiers, and produces ownership-verified asset records consumed by legal, insurance, and reporting services at Tier 5+. + +--- + +## Stack + +| Layer | Technology | +|-------|-----------| +| Runtime | Cloudflare Workers (migrating from Node.js 20 / Express 4 → Hono) | +| Deploy target | Cloudflare Workers (`chittyassets-api` / `chittyassets-api-prod`) | +| Frontend | React 18, Vite 7, Wouter, TanStack Query, Radix UI, Tailwind CSS 3 | +| Backend | Express 4, TypeScript 5.6, Drizzle ORM 0.39 | +| Database | Neon PostgreSQL (project: `steep-cloud-28172078`, org: Chitty(OS/APPS)Central) | +| Secret store | 1Password (`op://synthetic-shared/NEON_DB_CHITTYASSETS/credential`) | +| File storage | Google Cloud Storage (ACL-enforced, private evidence files) | +| AI | OpenAI GPT-4o (document analysis, valuation, legal document generation) | +| Auth middleware | ChittyAuth JWT (`auth.chitty.cc`) | +| Schema | Drizzle-Zod shared types in `/shared/schema.ts` | +| Package | `@chittyos/chittyassets` v1.0.0 | + +--- + +## Architecture + +``` +Browser / Mobile Client + | + React SPA (Vite) + | + Express API (Node.js) + | + ┌─────────────────────────────────────┐ + │ chittyAuth.ts → auth.chitty.cc │ JWT validation + │ chittyCore.ts → id.chitty.cc │ ChittyID generation + │ → trust.chitty.cc │ Trust scoring + │ → chain.chitty.cc │ Blockchain freeze/mint + │ → ledger.chitty.cc│ Evidence ledger + │ aiAnalysis.ts → OpenAI GPT-4o │ AI document analysis + │ objectStorage.ts→ Google Cloud │ Evidence file ACL + │ storage.ts → Neon PostgreSQL │ Structured data + └─────────────────────────────────────┘ + | + Cloudflare Worker (chittyassets-api-prod) +``` + +### Key Modules + +| Module | Path | Responsibility | +|--------|------|---------------| +| Route definitions | `server/routes.ts` | All API endpoints, auth middleware binding | +| ChittyOS core client | `server/chittyCore.ts` | Unified client for id/trust/chain/ledger services | +| ChittyAuth middleware | `server/chittyAuth.ts` | JWT validation against auth.chitty.cc | +| AI analysis | `server/aiAnalysis.ts` | GPT-4o receipt, document, valuation, legal doc generation | +| Object storage | `server/objectStorage.ts` | GCS signed URLs, ACL policy enforcement | +| Database layer | `server/storage.ts` | Drizzle ORM abstraction for all DB operations | +| Shared schema | `shared/schema.ts` | Zod + Drizzle schema shared by client and server | +| GitHub webhooks | `server/githubWebhooks.ts` | GitHub App event handling | +| Tool registry | `server/toolRegistry.ts` | Agent-accessible connector surface | + +--- + +## Consumers + +Services and actors that consume ChittyAssets: + +| Consumer | What they consume | +|----------|------------------| +| End users (browser/mobile) | React SPA — asset dashboard, evidence upload, AI analysis | +| ChittyChronicle | Audit event stream (timeline events written by assets service) | +| ChittyEvidence (legal) | Evidence records submitted to `ledger.chitty.cc` for legal case use | +| Agent layer / ChittyHelper | Tool resources via `/api/tools/resources` | +| ChittyScore | Asset trust scores fed into reputation calculations | + +--- + +## Asset Lifecycle State Machine + +``` +DRAFT → (freeze) → FROZEN → (7 days elapsed) → (mint) → MINTED + | | + | Evidence/AI + | analysis window + | + (delete) → removed +``` + +- **DRAFT**: Asset created, ChittyID assigned, trust score calculated. Evidence can be added, AI analysis can run. +- **FROZEN**: `POST /api/assets/:id/freeze` called. ChittyChain records freeze timestamp, IPFS hash assigned. 7-day immutability period begins. +- **MINTED**: After 7-day period, `POST /api/assets/:id/mint` creates evidence token on ChittyChain. `blockchainHash` and `mintingFee` recorded. + +--- + +## Evidence Compliance + +- **Legal standard**: Federal Rules of Evidence (FRE), USA Federal jurisdiction +- **Retention**: 2555 days (7 years) +- **Encryption at rest**: AES-256-GCM (configured via `.env.chittyos`) +- **Chain of custody**: All state transitions written as timeline events — immutable append +- **Digital signatures**: Required (configured via `.env.chittyos`: `CHITTY_DIGITAL_SIGNATURE_REQUIRED=true`) + +--- + +## Certification Level + +**BRONZE** (starting level — pending full registration and compliance sign-off) + +Bronze criteria met: +- ChittyAuth integration (in progress — uncommitted branch) +- ChittyID calls for asset identity generation +- ChittyTrust calls for trust scoring +- Neon PostgreSQL provisioned (project `steep-cloud-28172078`) +- Evidence ledger integration (`ledger.chitty.cc`) + +Bronze criteria NOT yet met (blockers for Silver): +- `/health` endpoint not present in routes (mandatory compliance endpoint) +- `/api/v1/status` endpoint not present in routes (mandatory compliance endpoint) +- `wrangler.toml` stale compat date (`2024-01-01`) +- Missing `[[hyperdrive]]` binding for Neon +- Missing `[[tail_consumers]]` for ChittyChronicle / ChittyTrack +- Not registered in ChittyCanon + +--- + +## Deployment + +```bash +# Development +npm run dev # Vite + Express hot reload + +# Build +npm run build # Vite frontend + esbuild server bundle + +# Deploy to Cloudflare +npx wrangler deploy --env production + +# Health check post-deploy +curl -s https://chittyassets-api-prod.workers.dev/health | jq . + +# Database schema push +npm run db:push +``` + +Secrets are injected via 1Password: +```bash +op run --env-file=.env.chittyos -- npm run dev +``` + +--- + +## Known Gaps (Wrangler / Infra) + +These are flagged in CHARTER.md and must be resolved before Silver certification: + +1. `compatibility_date = "2024-01-01"` — update to `2025-01-01` minimum +2. No `[[hyperdrive]]` block — add Neon Hyperdrive binding for pooled connections in Worker +3. No `[[tail_consumers]]` — add ChittyChronicle and ChittyTrack tail consumer bindings +4. KV and D1 blocks are commented out — confirm not needed or add with real IDs diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..a33e13d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,190 @@ +--- +uri: chittycanon://docs/gov/policy/security/chittyassets +namespace: chittycanon://docs/gov +type: policy +version: 1.0.0 +status: ACTIVE +registered_with: chittycanon://core/services/canon +title: "ChittyAssets Security Policy" +certifier: chittycanon://core/services/cert +visibility: PUBLIC +--- + +# Security Policy — ChittyAssets + +Service: `chittycanon://core/services/chittyassets` +Tier: 4 (Domain — asset ownership, evidence pipeline, blockchain integration) +Contact: security@chitty.cc + +--- + +## Reporting a Vulnerability + +**Do NOT open public GitHub issues for security bugs.** + +### Preferred: GitHub Security Advisory + +Open a private advisory at: + + +Include: +- Affected endpoint(s), route(s), or module(s) +- Step-by-step reproduction +- Impact assessment (data exposure, privilege escalation, evidence tampering, ACL bypass, etc.) +- Any suggested remediation or patch + +### Alternative: Email + +`security@chitty.cc` — PGP fingerprint available on request. + +--- + +## Response SLA + +| Stage | Target | +|---|---| +| Acknowledgement | 48 hours | +| Triage + severity classification | 5 business days | +| Fix — Critical severity | 14 days | +| Fix — High severity | 30 days | +| Fix — Medium / Low | Next scheduled release | +| Public disclosure | Coordinated with reporter; default 90 days post-fix | + +Reporters are credited in release notes and security advisories unless anonymity is requested. + +--- + +## Supported Versions + +ChittyAssets deploys continuously to Cloudflare Workers. There are no long-term-support branches. + +| Scope | Support window | +|---|---| +| Current deploy on `main` | Full support | +| Previous deploy on `main` | Patch support (critical only) | +| Any prior deploy | No support — upgrade required | + +Validate findings against the current deploy at the production Worker URL. The `compatibility_date` in `wrangler.toml` must be current — stale compat dates are a known open issue (see CHARTER.md compliance flags). + +--- + +## Severity Guidelines + +ChittyOS-adapted CVSS interpretation for a Tier 4 service handling asset ownership proofs and legal evidence: + +**Critical** — Unauthenticated access to evidence files or asset records; ACL bypass in GCS object streaming (`/objects/:objectPath`); JWT validation bypass in `requireChittyAuth()`; forged ChittyChain freeze or mint transactions; arbitrary code execution in the Worker or AI pipeline. + +**High** — Authenticated privilege escalation (user A accessing user B's assets or evidence); trust score manipulation via GPT-4o prompt injection on evidence payloads; evidence tampering after blockchain freeze; exfiltration of the Neon DB connection string or GCS credentials. + +**Medium** — Information disclosure of asset metadata to unauthorized users; denial of service against the evidence analysis pipeline; incorrect ACL policy applied to a GCS evidence file; timeline event forgery (non-cryptographic). + +**Low** — Best-practice violations with no demonstrable exploit; issues confined to local development paths; rate-limiting gaps with no demonstrated abuse path. + +--- + +## Threat Model Summary + +ChittyAssets sits at the intersection of four sensitive surfaces. Researchers should focus here: + +### 1. Evidence File ACL (Google Cloud Storage) + +Evidence files are private by default. Access is gated by `ObjectPermission.READ` checks in `server/objectStorage.ts` before any GCS stream is returned via `/objects/:objectPath`. + +**Risk**: An ACL policy misconfiguration or a bypassable ownership check would expose potentially legally privileged evidence files. + +**Mitigations**: Per-object ACL enforcement; all GCS access goes through `objectStorage.ts`; signed upload URLs (`/api/objects/upload`) have short TTLs. + +### 2. AI Analysis Pipeline (GPT-4o) + +`server/aiAnalysis.ts` sends user-supplied evidence content to OpenAI GPT-4o for analysis (receipt parsing, valuation, legal document generation). The analysis results feed the `trustScore` recorded on-chain. + +**Risk**: Prompt injection in evidence payloads could manipulate trust scores, generate fraudulent legal documents, or exfiltrate system prompt content. + +**Mitigations**: Analysis types are enumerated (`receipt`, `document`, `asset_valuation`); results are stored as structured JSON, not executed; trust score calculation is a downstream computation, not a direct AI output. + +### 3. Blockchain Integrity (ChittyChain Freeze / Mint) + +`POST /api/assets/:id/freeze` and `POST /api/assets/:id/mint` trigger calls to `chain.chitty.cc`. The 7-day freeze period is enforced by ChittyChain, not by this service. + +**Risk**: A race condition or replay attack on the freeze/mint endpoints could allow premature minting or double-minting. A forged response from `chain.chitty.cc` could record a false `blockchainHash`. + +**Mitigations**: Ownership check enforced before each state transition; `chittyChainStatus` field transitions are validated (`draft → frozen → minted`); all transitions are written as immutable timeline events. + +### 4. JWT Authentication (ChittyAuth) + +All protected routes require a valid JWT issued by `auth.chitty.cc` and validated in `server/chittyAuth.ts` via `requireChittyAuth()`. + +**Risk**: JWT validation bugs, algorithm confusion attacks, or token replay without expiry enforcement could grant unauthorized access. + +**Mitigations**: Validation delegated to `auth.chitty.cc`; tokens carry user scope; middleware applied uniformly via route registration in `server/routes.ts`. + +### 5. Evidence Ledger Submissions (ledger.chitty.cc) + +`POST /api/evidence-ledger/submit` forwards evidence to `chittycanon://core/services/evidence`. This is a write path to the ChittyOS shared evidence ledger. + +**Risk**: Submitting forged or tampered evidence records to the shared ledger could corrupt data consumed by downstream legal services. + +**Mitigations**: Evidence ChittyID is validated before submission; integrity is ultimately the responsibility of `chittycanon://core/services/evidence` — see that service's security policy. + +--- + +## Scope + +### In Scope + +- All API routes in `server/routes.ts` deployed to Cloudflare Workers +- `server/chittyAuth.ts` — JWT validation middleware +- `server/objectStorage.ts` — GCS ACL enforcement and signed URL generation +- `server/aiAnalysis.ts` — GPT-4o evidence analysis pipeline +- `server/chittyCore.ts` — ChittyOS upstream client (id/trust/chain/ledger calls) +- `server/storage.ts` — Neon DB query layer (SQL injection surface) +- `shared/schema.ts` — Zod validation schemas (input validation surface) +- CI/CD configurations under `.github/workflows/` that affect the Worker supply chain + +### Out of Scope + +- **Vendored dependencies** (`node_modules/`) — report upstream; we track and upgrade +- **Google Cloud Storage** itself — report to Google +- **OpenAI / GPT-4o** — report to OpenAI +- **Neon PostgreSQL** infrastructure — report to Neon +- **Cloudflare Workers runtime** — report to Cloudflare +- **ChittyAuth token issuance** — report to `chittycanon://core/services/auth` +- **ChittyChain blockchain** — report to `chittycanon://core/services/chain` +- Physical or social engineering attacks against ChittyOS personnel + +--- + +## Safe Harbor + +We will not pursue legal action against security researchers who: + +1. Make a good-faith effort to avoid privacy violations, data destruction, and service interruption. +2. Do not exfiltrate data beyond what is minimally necessary to demonstrate impact. +3. Do not attempt to access, modify, or destroy asset or evidence data belonging to other users. +4. Give us reasonable time to respond before any public disclosure. +5. Do not use findings to benefit third parties or harm users. + +Research that complies with this policy is considered authorized access under our terms of service. Evidence records touched during testing should be flagged to `security@chitty.cc` so they can be purged from the ledger. + +--- + +## Known Open Security Issues + +The following are disclosed and tracked — do not report these as new findings: + +1. **Stale `wrangler.toml` compat date** (`2024-01-01`) — suppresses runtime flag updates. Tracked in CHARTER.md. +2. **Missing `[[tail_consumers]]`** — audit event streaming to ChittyChronicle is not yet wired at the infrastructure level. Tracked in CHARTER.md. +3. **Auth migration** — CLAUDE.md references legacy Replit Auth; production code uses ChittyAuth. Full Replit Auth removal is a tracked compliance item. + +--- + +## Related Policies + +- **Incident response**: coordinated with `chittyops.chitty.cc` on Critical/High confirmation +- **Certificate revocation**: handled by `chittycanon://core/services/cert` if a fix invalidates issued certificates +- **Ecosystem advisories**: published through `registry.chitty.cc` and relayed to registered consumers +- **Evidence retention**: 2555 days (7 years), per FRE and USA Federal jurisdiction — do not request deletion of evidence records under legal hold + +--- + +*Last reviewed: 2026-05-10. This policy is a living document; breaking changes bump the `version` field in the front-matter.* diff --git a/client/src/lib/chittyAuthUrls.ts b/client/src/lib/chittyAuthUrls.ts new file mode 100644 index 0000000..6b9e0fc --- /dev/null +++ b/client/src/lib/chittyAuthUrls.ts @@ -0,0 +1,46 @@ +const DEFAULT_CHITTYAUTH_ORIGIN = 'https://auth.chitty.cc'; + +function normalizeOrigin(origin: string | undefined): string { + if (!origin) return DEFAULT_CHITTYAUTH_ORIGIN; + return origin.replace(/\/+$/, ''); +} + +export function getChittyAuthOrigin(): string { + return normalizeOrigin(import.meta.env.VITE_CHITTYAUTH_ORIGIN); +} + +export function getReturnToUrl(): string { + return window.location.origin; +} + +function buildAuthUrl(path: string, params: Record): string { + const url = new URL(path, `${getChittyAuthOrigin()}/`); + for (const [k, v] of Object.entries(params)) { + url.searchParams.set(k, v); + } + return url.toString(); +} + +export function getSignInUrl(): string { + return buildAuthUrl('/sign-in', { return_to: getReturnToUrl() }); +} + +export function getSignUpUrl(): string { + return buildAuthUrl('/sign-up', { return_to: getReturnToUrl() }); +} + +export function redirectToSignIn(): void { + window.location.href = getSignInUrl(); +} + +export function redirectToSignUp(): void { + window.location.href = getSignUpUrl(); +} + +export function getSignOutUrl(): string { + return buildAuthUrl('/sign-out', { return_to: getReturnToUrl() }); +} + +export function redirectToSignOut(): void { + window.location.href = getSignOutUrl(); +} diff --git a/package-lock.json b/package-lock.json index 69eaae0..7374224 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@clerk/express": "^2.0.1", "@clerk/types": "^4.0.0", "@google-cloud/storage": "^7.16.0", + "@hono/zod-validator": "^0.8.0", "@hookform/resolvers": "^3.10.0", "@jridgewell/trace-mapping": "^0.3.25", "@neondatabase/serverless": "^0.10.4", @@ -69,7 +70,9 @@ "express-session": "^1.19.0", "framer-motion": "^11.13.1", "google-auth-library": "^10.2.1", + "hono": "^4.12.18", "input-otp": "^1.4.2", + "jose": "^6.2.3", "lucide-react": "^0.453.0", "memoizee": "^0.4.17", "memorystore": "^1.6.7", @@ -144,6 +147,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -1565,6 +1569,16 @@ "hono": "^4" } }, + "node_modules/@hono/zod-validator": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@hono/zod-validator/-/zod-validator-0.8.0.tgz", + "integrity": "sha512-5uS4S1/LKtZQYvD4BtpPUFkOv8d1wNxHHrChm26buMiEYc1FrHWvDUaKVBwkiVtvSExHSpLGDvcnpI2Copyj9w==", + "license": "MIT", + "peerDependencies": { + "hono": ">=4.10.0", + "zod": "^3.25.0 || ^4.0.0" + } + }, "node_modules/@hookform/resolvers": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", @@ -1862,6 +1876,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -1889,6 +1904,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -2305,6 +2321,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -2318,6 +2335,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -2327,6 +2345,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -2353,6 +2372,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -5270,7 +5290,7 @@ "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/@types/qs": { @@ -5291,7 +5311,7 @@ "version": "18.3.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -5302,7 +5322,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@types/react": "*" @@ -6263,6 +6283,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6275,6 +6296,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6287,12 +6309,14 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -6306,6 +6330,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, "license": "MIT" }, "node_modules/aria-hidden": { @@ -6467,6 +6492,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -6512,6 +6538,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6592,6 +6619,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -6601,6 +6629,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -6717,6 +6746,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -6770,6 +6800,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -6794,6 +6825,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -6863,6 +6895,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -6875,6 +6908,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/colorette": { @@ -6899,6 +6933,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -7017,6 +7052,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -7355,6 +7391,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, "license": "Apache-2.0" }, "node_modules/difflib": { @@ -7373,6 +7410,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, "license": "MIT" }, "node_modules/dom-accessibility-api": { @@ -7744,6 +7782,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, "license": "MIT" }, "node_modules/ecdsa-sig-formatter": { @@ -7797,6 +7836,7 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, "license": "MIT" }, "node_modules/encodeurl": { @@ -8626,6 +8666,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -8642,6 +8683,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -8709,6 +8751,7 @@ "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -8748,6 +8791,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -8820,6 +8864,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", @@ -8931,6 +8976,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -9098,6 +9144,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -9118,6 +9165,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -9329,9 +9377,9 @@ "license": "MIT" }, "node_modules/hono": { - "version": "4.12.7", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", - "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==", + "version": "4.12.18", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.18.tgz", + "integrity": "sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -9508,6 +9556,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -9520,6 +9569,7 @@ "version": "2.15.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -9535,6 +9585,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9544,6 +9595,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9553,6 +9605,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -9583,6 +9636,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -9677,6 +9731,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -9692,15 +9747,16 @@ "version": "1.21.6", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, "license": "MIT", "bin": { "jiti": "bin/jiti.js" } }, "node_modules/jose": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.0.tgz", - "integrity": "sha512-xsfE1TcSCbUdo6U07tR0mvhg0flGxU8tPLbF03mirl2ukGQENhUg4ubGYQnhVH0b5stLlPM+WOqDkEl1R1y5sQ==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -10174,6 +10230,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, "engines": { "node": ">=14" }, @@ -10185,6 +10242,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, "license": "MIT" }, "node_modules/lodash": { @@ -10435,6 +10493,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -10453,6 +10512,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -10518,6 +10578,7 @@ "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.2" @@ -10542,6 +10603,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -10583,6 +10645,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0", @@ -10600,6 +10663,7 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, "funding": [ { "type": "github", @@ -10711,6 +10775,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10748,6 +10813,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -10925,6 +10991,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parse5": { @@ -10962,12 +11029,14 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -10984,6 +11053,7 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, "license": "ISC" }, "node_modules/path-to-regexp": { @@ -11158,12 +11228,14 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -11176,6 +11248,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -11246,6 +11319,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -11264,6 +11338,7 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -11292,6 +11367,7 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", @@ -11309,6 +11385,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" @@ -11328,6 +11405,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -11363,6 +11441,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -11388,6 +11467,7 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -11401,6 +11481,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, "license": "MIT" }, "node_modules/postgres-array": { @@ -11610,6 +11691,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -11880,6 +11962,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, "license": "MIT", "dependencies": { "pify": "^2.3.0" @@ -11903,6 +11986,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -11992,6 +12076,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", @@ -12042,6 +12127,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -12129,6 +12215,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -12410,6 +12497,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -12477,6 +12565,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -12551,6 +12640,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -12569,6 +12659,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -12583,6 +12674,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12592,12 +12684,14 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12610,6 +12704,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -12626,6 +12721,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12638,6 +12734,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12690,6 +12787,7 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", @@ -12725,6 +12823,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -12766,6 +12865,7 @@ "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -12893,6 +12993,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0" @@ -12902,6 +13003,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" @@ -13037,6 +13139,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -13087,6 +13190,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, "license": "Apache-2.0" }, "node_modules/tslib": { @@ -13725,6 +13829,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -13743,6 +13848,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -13760,6 +13866,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -13769,6 +13876,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -13784,12 +13892,14 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -13804,6 +13914,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -13876,6 +13987,7 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index 5b51eea..f5a55c1 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@clerk/express": "^2.0.1", "@clerk/types": "^4.0.0", "@google-cloud/storage": "^7.16.0", + "@hono/zod-validator": "^0.8.0", "@hookform/resolvers": "^3.10.0", "@jridgewell/trace-mapping": "^0.3.25", "@neondatabase/serverless": "^0.10.4", @@ -95,7 +96,9 @@ "express-session": "^1.19.0", "framer-motion": "^11.13.1", "google-auth-library": "^10.2.1", + "hono": "^4.12.18", "input-otp": "^1.4.2", + "jose": "^6.2.3", "lucide-react": "^0.453.0", "memoizee": "^0.4.17", "memorystore": "^1.6.7", diff --git a/register.json b/register.json new file mode 100644 index 0000000..ce49e9e --- /dev/null +++ b/register.json @@ -0,0 +1,281 @@ +{ + "uri": "chittycanon://core/services/chittyassets", + "canonical_uri": "chittycanon://core/services/chittyassets", + "name": "chittyassets", + "chitty_id": null, + "description": "Blockchain-anchored asset ownership registry and AI-verified evidence ledger for the ChittyOS ecosystem. Registers physical and digital assets, attaches GPT-4o analyzed evidence, enforces a 7-day immutability freeze before blockchain finalization, and exposes ownership proofs for downstream legal and insurance services.", + "version": "1.0.0", + "tier": 4, + "organization": "CHITTYOS", + "repository": "https://github.com/CHITTYOS/chittyassets", + "primary_url": "https://assets.chitty.cc", + "health_endpoint": "https://assets.chitty.cc/health", + "status_endpoint": "https://assets.chitty.cc/api/v1/status", + "entity_types_handled": ["P", "L", "T", "E", "A"], + "endpoints": [ + { + "path": "/health", + "method": "GET", + "auth": "none", + "description": "Health check — returns 200 with service status" + }, + { + "path": "/api/v1/status", + "method": "GET", + "auth": "none", + "description": "Service status and version" + }, + { + "path": "/api/auth/user", + "method": "GET", + "auth": "jwt", + "description": "Fetch authenticated user record" + }, + { + "path": "/api/assets", + "method": "GET", + "auth": "jwt", + "description": "List user assets with optional filters" + }, + { + "path": "/api/assets", + "method": "POST", + "auth": "jwt", + "description": "Create asset with ChittyID generation and trust score calculation" + }, + { + "path": "/api/assets/stats", + "method": "GET", + "auth": "jwt", + "description": "Aggregate portfolio statistics" + }, + { + "path": "/api/assets/:id", + "method": "GET", + "auth": "jwt", + "description": "Fetch single asset by ID" + }, + { + "path": "/api/assets/:id", + "method": "PUT", + "auth": "jwt", + "description": "Update asset fields" + }, + { + "path": "/api/assets/:id", + "method": "DELETE", + "auth": "jwt", + "description": "Delete asset" + }, + { + "path": "/api/assets/:id/freeze", + "method": "POST", + "auth": "jwt", + "description": "Initiate 7-day ChittyChain immutability freeze" + }, + { + "path": "/api/assets/:id/mint", + "method": "POST", + "auth": "jwt", + "description": "Mint evidence token on ChittyChain (requires frozen status)" + }, + { + "path": "/api/assets/:assetId/evidence", + "method": "GET", + "auth": "jwt", + "description": "List evidence attached to asset" + }, + { + "path": "/api/assets/:assetId/evidence", + "method": "POST", + "auth": "jwt", + "description": "Attach evidence record to asset" + }, + { + "path": "/api/evidence/:evidenceId/analyze", + "method": "POST", + "auth": "jwt", + "description": "GPT-4o AI analysis of evidence (receipt, document, asset_valuation)" + }, + { + "path": "/api/evidence-ledger/submit", + "method": "POST", + "auth": "jwt", + "description": "Submit evidence to ChittyOS evidence ledger (ledger.chitty.cc)" + }, + { + "path": "/api/evidence-ledger/:chittyId", + "method": "GET", + "auth": "jwt", + "description": "Retrieve evidence by ChittyID from ledger" + }, + { + "path": "/api/evidence-ledger/:chittyId/verify", + "method": "POST", + "auth": "jwt", + "description": "Verify evidence authenticity via ledger" + }, + { + "path": "/api/assets/:assetId/timeline", + "method": "GET", + "auth": "jwt", + "description": "Asset lifecycle event timeline" + }, + { + "path": "/api/assets/:assetId/warranties", + "method": "GET", + "auth": "jwt", + "description": "List warranties for asset" + }, + { + "path": "/api/assets/:assetId/warranties", + "method": "POST", + "auth": "jwt", + "description": "Create warranty record" + }, + { + "path": "/api/warranties/expiring", + "method": "GET", + "auth": "jwt", + "description": "List expiring warranties within N days" + }, + { + "path": "/api/assets/:assetId/insurance", + "method": "GET", + "auth": "jwt", + "description": "List insurance policies for asset" + }, + { + "path": "/api/assets/:assetId/insurance", + "method": "POST", + "auth": "jwt", + "description": "Attach insurance policy to asset" + }, + { + "path": "/api/assets/:assetId/calculate-trust-score", + "method": "POST", + "auth": "jwt", + "description": "Recalculate trust score via AI analysis of current evidence" + }, + { + "path": "/api/legal-cases", + "method": "GET", + "auth": "jwt", + "description": "List user legal cases" + }, + { + "path": "/api/legal-cases", + "method": "POST", + "auth": "jwt", + "description": "Create legal case" + }, + { + "path": "/api/legal/generate-document", + "method": "POST", + "auth": "jwt", + "description": "AI-generate legal ownership or chain-of-custody document" + }, + { + "path": "/api/ecosystem/status", + "method": "GET", + "auth": "jwt", + "description": "Aggregated health of all ChittyOS upstream services" + }, + { + "path": "/api/tools/resources", + "method": "GET", + "auth": "jwt", + "description": "List tool and connector resources available to the agent layer" + }, + { + "path": "/objects/:objectPath", + "method": "GET", + "auth": "jwt", + "description": "Stream evidence file from R2 (ACL-enforced via Neon)" + }, + { + "path": "/api/objects/upload", + "method": "POST", + "auth": "jwt", + "description": "Get presigned R2 upload URL for evidence file" + }, + { + "path": "/api/evidence-files", + "method": "PUT", + "auth": "jwt", + "description": "Set ACL policy on uploaded evidence file" + } + ], + "schema": { + "version": "1.0.0", + "entities": [ + "users", + "assets", + "evidence", + "aiAnalysisResults", + "timelineEvents", + "warranties", + "insurancePolicies", + "legalCases" + ], + "relationships": [ + "chittycanon://rel/asset-has-evidence", + "chittycanon://rel/asset-has-timeline", + "chittycanon://rel/asset-has-warranty", + "chittycanon://rel/asset-has-insurance", + "chittycanon://rel/evidence-has-ai-analysis", + "chittycanon://rel/user-owns-asset", + "chittycanon://rel/user-has-legal-case" + ] + }, + "security": { + "authentication": "jwt", + "encryption": "tls", + "authProvider": "chittycanon://core/services/auth", + "evidenceEncryption": "AES-256-GCM", + "fileAcl": "private-by-default" + }, + "dependencies": { + "upstream": [ + "chittycanon://core/services/auth", + "chittycanon://core/services/identity", + "chittycanon://core/services/trust", + "chittycanon://core/services/schema", + "chittycanon://core/services/chain", + "chittycanon://core/services/evidence" + ], + "downstream": [ + "chittycanon://core/services/chronicle", + "chittycanon://core/services/discovery" + ], + "peer": [ + "chittycanon://core/services/chittyscore" + ], + "external": [ + "openai-gpt4o", + "cloudflare-r2", + "neon-postgresql" + ] + }, + "compliance": { + "legalFramework": "USA_FEDERAL", + "evidenceStandard": "FRE", + "retentionDays": 2555, + "chainOfCustody": "strict", + "jurisdiction": "USA" + }, + "deployment": { + "platform": "cloudflare-workers", + "workerName": "chittyassets-api-prod", + "route": "assets.chitty.cc/*", + "zone": "chitty.cc", + "compatibilityDate": "2026-03-28", + "compatibilityFlags": ["nodejs_compat"], + "framework": "hono", + "migrationStatus": "MIGRATING_EXPRESS_TO_HONO" + }, + "observability": { + "tailConsumers": ["chittytrack"] + }, + "certificationLevel": "BRONZE" +} diff --git a/worker/src/auth.ts b/worker/src/auth.ts new file mode 100644 index 0000000..aa0288d --- /dev/null +++ b/worker/src/auth.ts @@ -0,0 +1,101 @@ +// @canon: chittycanon://core/services/auth +// JWKS-based JWT verification for ChittyAuth-issued tokens. +// Per CHARTER §Security: middleware on all protected routes. + +import { createRemoteJWKSet, errors as joseErrors, jwtVerify } from "jose"; +import type { Context, MiddlewareHandler } from "hono"; +import { ChittyAuthClaimsSchema, type ChittyAuthClaims, type Env } from "./env"; + +const JWKS_TIMEOUT_MS = 5000; +const JWKS_COOLDOWN_MS = 30000; + +let cachedJWKS: ReturnType | null = null; +let cachedJWKSUrl: string | null = null; + +function getJWKS(jwksUrl: string) { + if (!cachedJWKS || cachedJWKSUrl !== jwksUrl) { + cachedJWKS = createRemoteJWKSet(new URL(jwksUrl), { + timeoutDuration: JWKS_TIMEOUT_MS, + cooldownDuration: JWKS_COOLDOWN_MS, + }); + cachedJWKSUrl = jwksUrl; + } + return cachedJWKS; +} + +function extractBearer(c: Context): string | null { + const header = c.req.header("authorization") || c.req.header("Authorization"); + if (header?.startsWith("Bearer ")) return header.slice(7); + // JWTs are base64url — never URL-encoded. No decode needed. + const cookie = c.req.header("cookie"); + if (cookie) { + const m = cookie.match(/(?:^|;\s*)chitty_jwt=([^;\s]+)/); + if (m) return m[1]; + } + return null; +} + +function logReject(c: Context, reason: string, detail?: string) { + // Tail consumer (chittytrack) captures these; no PII beyond reason code. + console.error("auth_reject", { + reason, + detail, + path: c.req.path, + method: c.req.method, + ip: c.req.header("cf-connecting-ip") ?? null, + }); +} + +export const requireChittyAuth: MiddlewareHandler<{ Bindings: Env; Variables: { claims: ChittyAuthClaims } }> = + async (c, next) => { + const token = extractBearer(c); + if (!token) { + logReject(c, "missing_token"); + return c.json({ error: "unauthenticated" }, 401); + } + + let payload: unknown; + try { + const verified = await jwtVerify(token, getJWKS(c.env.CHITTYAUTH_JWKS_URL), { + issuer: c.env.CHITTYAUTH_ISSUER, + audience: c.env.CHITTYAUTH_AUDIENCE, + }); + payload = verified.payload; + } catch (err) { + // Distinguish infra (fail closed but signal ops) from validation (client problem). + if (err instanceof joseErrors.JWKSNoMatchingKey || err instanceof joseErrors.JWKSMultipleMatchingKeys) { + logReject(c, "jwks_key_mismatch", (err as Error).name); + return c.json({ error: "unauthenticated" }, 401); + } + if (err instanceof joseErrors.JWTExpired) { + logReject(c, "token_expired"); + return c.json({ error: "token_expired" }, 401); + } + if (err instanceof joseErrors.JWTClaimValidationFailed || err instanceof joseErrors.JWTInvalid) { + logReject(c, "token_invalid", (err as Error).name); + return c.json({ error: "unauthenticated" }, 401); + } + // Anything else is likely infra (JWKS unreachable, timeout). Log loudly, surface generic 503. + console.error("auth_jwks_unavailable", { + path: c.req.path, + error: (err as Error).name, + message: (err as Error).message, + }); + return c.json({ error: "auth_service_unavailable" }, 503); + } + + const parsed = ChittyAuthClaimsSchema.safeParse(payload); + if (!parsed.success) { + logReject(c, "claims_schema", parsed.error.issues[0]?.code); + return c.json({ error: "unauthenticated" }, 401); + } + + // Per chittycanon://gov/governance — authenticated principals are Person (P), never Thing. + if (parsed.data.entity_type !== "P") { + logReject(c, "principal_not_person", parsed.data.entity_type); + return c.json({ error: "forbidden" }, 403); + } + + c.set("claims", parsed.data); + return await next(); + }; diff --git a/worker/src/env.ts b/worker/src/env.ts new file mode 100644 index 0000000..9ab9b13 --- /dev/null +++ b/worker/src/env.ts @@ -0,0 +1,44 @@ +// @canon: chittycanon://core/services/chittyassets +// Typed bindings for the chittyassets Worker. Must match wrangler.jsonc exactly. + +import { z } from "zod"; + +// Per chittycanon://gov/governance#core-types — all five must be enumerated, never omitted. +export const ENTITY_TYPES = ["P", "L", "T", "E", "A"] as const; +export type EntityType = typeof ENTITY_TYPES[number]; + +// ChittyID format: VV-G-LLL-SSSS-T-YM-C-X +export const CHITTY_ID_PATTERN = /^[A-Z0-9]{2}-[A-Z0-9]-[A-Z0-9]{3}-[A-Z0-9]{4}-[PLTEA]-[A-Z0-9]{2}-[A-Z0-9]-[A-Z0-9]$/; + +export interface Env { + ENVIRONMENT: "development" | "production"; + CHITTYAUTH_ISSUER: string; + CHITTYAUTH_JWKS_URL: string; + CHITTYAUTH_AUDIENCE: string; + CHITTYMINT_URL?: string; + CHITTYCONNECT_URL?: string; + CHITTYLEDGER_URL?: string; + + // Phase 2+ bindings: + // CHITTYASSETS_DB: Hyperdrive; + // EVIDENCE: R2Bucket; + // PROCESSED: R2Bucket; + // CHITTYCONNECT: Fetcher; +} + +export const ChittyAuthClaimsSchema = z + .object({ + iss: z.string().url(), + sub: z.string().regex(CHITTY_ID_PATTERN), + chitty_id: z.string().regex(CHITTY_ID_PATTERN), + entity_type: z.enum(ENTITY_TYPES), + trust_level: z.number().int().min(0).max(5), + exp: z.number().int(), + iat: z.number().int(), + email: z.string().email().optional(), + aud: z.union([z.string(), z.array(z.string())]).optional(), + }) + .refine((c) => c.sub === c.chitty_id, { message: "sub must equal chitty_id" }) + .refine((c) => c.exp > c.iat, { message: "exp must be after iat" }); + +export type ChittyAuthClaims = z.infer; diff --git a/worker/src/index.ts b/worker/src/index.ts new file mode 100644 index 0000000..0738100 --- /dev/null +++ b/worker/src/index.ts @@ -0,0 +1,106 @@ +// @canon: chittycanon://core/services/chittyassets +// chittyassets Worker entrypoint (Hono). Tier 4 Domain service. +// Phase 1 of Express→Hono migration: skeleton + auth + health. + +import { Hono } from "hono"; +import { cors } from "hono/cors"; +import { logger } from "hono/logger"; +import { ENTITY_TYPES, type ChittyAuthClaims, type Env } from "./env"; +import { requireChittyAuth } from "./auth"; + +type Variables = { claims: ChittyAuthClaims }; + +const ALLOWED_ORIGINS = new Set([ + "https://assets.chitty.cc", + "https://chitty.cc", + "https://www.chitty.cc", + "http://localhost:5173", + "http://localhost:5000", +]); + +function corsOrigin(origin: string | undefined): string | null { + if (!origin) return null; + return ALLOWED_ORIGINS.has(origin) ? origin : null; +} + +const app = new Hono<{ Bindings: Env; Variables: Variables }>(); + +app.use("*", logger()); +app.use("*", cors({ + origin: corsOrigin, + credentials: true, + allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allowHeaders: ["Content-Type", "Authorization"], +})); + +app.get("/health", (c) => + c.json({ + status: "ok", + service: "chittyassets", + tier: 4, + canonical_uri: "chittycanon://core/services/chittyassets", + version: "1.0.0", + environment: c.env.ENVIRONMENT, + timestamp: new Date().toISOString(), + }), +); + +app.get("/api/v1/status", (c) => + c.json({ + status: "ok", + service: "chittyassets", + tier: 4, + canonical_uri: "chittycanon://core/services/chittyassets", + version: "1.0.0", + environment: c.env.ENVIRONMENT, + migration_status: "MIGRATING_EXPRESS_TO_HONO", + entity_types_handled: [...ENTITY_TYPES], + dependencies: { + chittyauth: c.env.CHITTYAUTH_ISSUER, + chittymint: c.env.CHITTYMINT_URL ?? null, + chittyconnect: c.env.CHITTYCONNECT_URL ?? null, + chittyledger: c.env.CHITTYLEDGER_URL ?? null, + }, + timestamp: new Date().toISOString(), + }), +); + +app.get("/api/auth/user", requireChittyAuth, (c) => { + const claims = c.get("claims"); + return c.json({ + chitty_id: claims.chitty_id, + entity_type: claims.entity_type, + trust_level: claims.trust_level, + email: claims.email ?? null, + }); +}); + +// Unmigrated routes return 501 unconditionally — no auth oracle. +app.all("/api/*", (c) => + c.json( + { + error: "not_yet_migrated", + message: "Route not yet migrated from Express to Hono. See CHARTER.md §Compliance Flags.", + path: c.req.path, + }, + 501, + ), +); + +app.notFound((c) => c.json({ error: "not_found", path: c.req.path }, 404)); + +app.onError((err, c) => { + const correlationId = crypto.randomUUID(); + // Full error to tail consumer; generic message to client. + console.error("worker_error", { + correlation_id: correlationId, + name: err.name, + message: err.message, + stack: err.stack, + path: c.req.path, + method: c.req.method, + }); + return c.json({ error: "internal_error", correlation_id: correlationId }, 500); +}); + +export default app; diff --git a/wrangler.jsonc b/wrangler.jsonc new file mode 100644 index 0000000..2a396f3 --- /dev/null +++ b/wrangler.jsonc @@ -0,0 +1,72 @@ +// chittyassets — Cloudflare Workers configuration (backend API) +// Frontend SPA (Cloudflare Pages) is deployed separately from `dist/public`. +{ + "$schema": "https://json.schemastore.org/wrangler.json", + "name": "chittyassets-api", + "account_id": "0bc21e3a5a9de1a4cc843be9c3e98121", + "main": "worker/src/index.ts", + "compatibility_date": "2026-03-28", + "compatibility_flags": ["nodejs_compat"], + "workers_dev": true, + "observability": { + "enabled": true + }, + "tail_consumers": [ + { "service": "chittytrack" } + ], + "vars": { + "ENVIRONMENT": "development", + "CHITTYAUTH_ISSUER": "https://auth.chitty.cc", + "CHITTYAUTH_JWKS_URL": "https://auth.chitty.cc/.well-known/jwks.json", + "CHITTYAUTH_AUDIENCE": "chittyassets-api", + "CHITTYMINT_URL": "https://mint.chitty.cc", + "CHITTYCONNECT_URL": "https://connect.chitty.cc", + "CHITTYLEDGER_URL": "https://ledger.chitty.cc" + }, + "env": { + "production": { + "name": "chittyassets-api-prod", + "routes": [ + { "pattern": "assets.chitty.cc/*", "zone_name": "chitty.cc" } + ], + "workers_dev": false, + "observability": { + "enabled": true + }, + "tail_consumers": [ + { "service": "chittytrack" } + ], + "vars": { + "ENVIRONMENT": "production", + "CHITTYAUTH_ISSUER": "https://auth.chitty.cc", + "CHITTYAUTH_JWKS_URL": "https://auth.chitty.cc/.well-known/jwks.json", + "CHITTYAUTH_AUDIENCE": "chittyassets-api", + "CHITTYMINT_URL": "https://mint.chitty.cc", + "CHITTYCONNECT_URL": "https://connect.chitty.cc", + "CHITTYLEDGER_URL": "https://ledger.chitty.cc" + } + , + "hyperdrive": [ + { "binding": "CHITTYASSETS_DB", "id": "4bd7964c46dd42be86e8a5e3dd0d7376" } + ], + "assets": { + "directory": "dist/public", + "binding": "ASSETS", + "not_found_handling": "single-page-application" + } + // Phase 4 bindings: + // "r2_buckets": [ + // { "binding": "EVIDENCE", "bucket_name": "chittyassets-evidence" }, + // { "binding": "PROCESSED", "bucket_name": "chittyassets-processed" } + // ], + // "services": [{ "binding": "CHITTYCONNECT", "service": "chittyconnect" }] + } + }, + "dev": { + "port": 8788, + "local_protocol": "http" + }, + "limits": { + "cpu_ms": 60000 + } +} diff --git a/wrangler.toml b/wrangler.toml deleted file mode 100644 index 61eca6c..0000000 --- a/wrangler.toml +++ /dev/null @@ -1,27 +0,0 @@ -# Cloudflare Pages configuration -pages_build_output_dir = "dist/public" - -# Worker configuration for backend API -name = "chittyassets-api" -main = "dist/index.js" -compatibility_date = "2024-01-01" -compatibility_flags = ["nodejs_compat"] - -[env.production] -name = "chittyassets-api-prod" - -# Environment variables -[vars] -NODE_ENV = "production" - -# KV namespaces for session storage if needed -# [[kv_namespaces]] -# binding = "SESSIONS" -# id = "your-kv-namespace-id" -# preview_id = "your-preview-kv-namespace-id" - -# D1 database bindings if needed -# [[d1_databases]] -# binding = "DB" -# database_name = "chittyassets" -# database_id = "your-database-id" \ No newline at end of file