Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Repository Guidelines

## Project Structure & Module Organization

- ESM JavaScript Cloudflare Worker. No TypeScript build step.
- Entry point: `worker.js` (fetch handler).
- Source under `src/`:
- `api-router.js` — request routing and endpoint dispatch
- `token-manager.js` — token generation, hashing, validation, lifecycle
- `registration-handler.js` — public `/v1/register` flow
- `auth-provider.js` — provider facade selecting `local` (D1+KV) or `neon` (Neon OAuth) per `CHITTYAUTH_PROVIDER`
- `chittyconnect-client.js` — optional ChittyConnect integration
- Schema: `schema.sql` (initial; defines `tokens`, `service_credentials`, `auth_events`, `token_stats`, `service_health`), `schema-update.sql` (adds `registrations` etc.).
- Tests: `tests/*.test.js` (Jest, currently flat — only `tests/token-manager.test.js`).
- Setup helpers: `scripts/` (currently `onboard.sh`).
- Config: `wrangler.toml`, `package.json`.

## Build, Test, and Development Commands

- `npm install` — install dev dependencies.
- `npm run dev` — local Worker via `wrangler dev` (defaults to `http://localhost:8787`).
- `npm test` — run Jest suite (`node --experimental-vm-modules`).
- `npm run test:unit` / `npm run test:integration` — **aspirational**: scripts exist but `tests/unit/` and `tests/integration/` directories do not yet. Until the suite is split, these run zero tests; use `npm test`.
- `npm run setup:db` — apply `schema.sql` to the production D1 database.
- `npm run setup:kv` — **broken**: invokes `scripts/setup-kv.js` which does not exist (only `scripts/onboard.sh` ships today). Provision KV manually via `wrangler kv:namespace create` until the script lands.
- `npm run setup` — runs the above two; will fail at `setup:kv` until the script is added.
- `npm run deploy` — deploy production environment.
- `npm run deploy:dev` — deploy development environment.

There is no `lint`, `typecheck`, `format`, or `build` script today. Do not invent them; either add them as a separate, scoped change or skip.

## Coding Style & Naming Conventions

- ESM (`"type": "module"`). 2-space indent, single quotes, semicolons.
- Files: lower-kebab (`api-router.js`, `token-manager.js`).
- Functions: `camelCase`. Classes: `PascalCase`. Constants: `UPPER_SNAKE_CASE`.
- Async-first; prefer `await` over `.then()` chains.
- No external runtime dependencies (`dependencies: {}` is intentional). Use Web Crypto, `fetch`, and Workers bindings only.

## Testing Guidelines

- Jest with `--experimental-vm-modules` (ESM). File pattern: `tests/**/*.test.js`.
- Tests must exercise real behavior. Per repo policy, do not introduce new `jest.mock()` on D1, KV, or service modules — use `wrangler dev --local` for storage-backed tests, or hit a disposable D1 branch.
- Token-related tests must cover: signature verification, hash determinism, revocation precedence, expiration boundary, KV cache hit/miss.

## Commit & Pull Request Guidelines

- Conventional Commits (`feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:`).
- Before pushing: `npm test` must pass; smoke-test `/health` and `/v1/register` against `wrangler dev --local`.
- PR body must include: scope of change, test evidence, any `wrangler.toml` binding deltas, and confirmation that no plaintext tokens or signing keys appear in diffs/logs.

## Security & Configuration Tips

- **Never commit any secret.** Canonical names: `CHITTYAUTH_ISSUED_MINT_API_KEY` (signing key) and `CHITTYAUTH_ISSUED_CONNECT_API_KEY` (when ChittyConnect is wired). Set via `wrangler secret put <NAME> --env <env>`. Legacy aliases `TOKEN_SIGNING_KEY` and `CHITTYCONNECT_API_KEY` remain accepted only for migration and must be retired post-cutover.
- The committed `wrangler.toml` still contains `CREATE_NEW_*` / `CREATE_DEV_*` placeholders for D1 and KV IDs. Do not deploy until those are replaced with real binding IDs.
- Tokens are returned to callers exactly once at issuance; storage is SHA-256 hash of the token only. Do not add code paths that log, return, or persist plaintext tokens.
- Rate limiting and revocation are correctness features, not best-effort: validate that new endpoints honor `AUTH_RATE_LIMITS` and check `AUTH_REVOCATIONS` before trusting a token.
- Optional ChittyConnect integration is gated on the connect secret; code paths should fall closed (deny) when the integration is configured but unreachable, and fall open only when integration is unconfigured by design. Verify behavior in `src/chittyconnect-client.js` before relying on this contract.
14 changes: 9 additions & 5 deletions CHARTER.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ ChittyAuth App is a **standalone authentication and token provisioning service**
- HMAC-SHA256 signing + SHA-256 hashed-at-rest token storage
- KV-first validation cache (30s TTL) and revocation blocklist
- Per-token rate limiting via KV counters (1h window)
- Append-only audit logging (D1 `audit_logs`)
- Append-only audit logging (D1 `auth_events`)
- OAuth client registration

### IS NOT Responsible For
Expand All @@ -41,14 +41,18 @@ ChittyAuth App is a **standalone authentication and token provisioning service**
## Architecture

### Storage Bindings
- **D1** (`AUTH_DB`): `users`, `api_tokens`, `audit_logs`, `oauth_clients`
- **D1** (`AUTH_DB`): `tokens`, `service_credentials`, `auth_events`, `token_stats`, `service_health`, `registrations` (from `schema-update.sql`)
- **KV**:
- `AUTH_TOKENS` — validation cache (30s TTL)
- `AUTH_REVOCATIONS` — revoked-token blocklist
- `AUTH_RATE_LIMITS` — per-token request counters (1h window)
- `AUTH_AUDIT` — audit-log buffer

### Validation Flow
### Provider Modes
- `CHITTYAUTH_PROVIDER=local` (default) — D1+KV-backed token authority described above.
- `CHITTYAUTH_PROVIDER=neon` — Neon OAuth facade (`src/auth-provider.js`); `api-router.js` exposes authorize/exchange endpoints. The local validation flow below applies to provider=local; the Neon path delegates issuance to Neon's OAuth endpoint.

### Validation Flow (provider=local)
```
Request → KV cache (fast path) ──hit──→ return
│ miss
Expand Down Expand Up @@ -129,7 +133,7 @@ Operational gate (must be green before deploy):
- [ ] All four KV namespaces created and bound in `wrangler.toml`
- [ ] `CHITTYAUTH_ISSUED_MINT_API_KEY` set via `wrangler secret put`
- [ ] If Neon-backed mode is enabled: `CHITTYAUTH_PROVIDER=neon` and Neon OAuth secrets are present
- [ ] `/health` returns `{"status":"healthy"}` with `checks.database` and `checks.kv` true
- [ ] `/health` returns `{"status":"healthy"}` with `dependencies.chittyConnect === "healthy"` (current shape per `api-router.js:392-403`; richer `checks.database`/`checks.kv` reporting is a future health-endpoint enhancement)
- [ ] `/v1/register` smoke test succeeds end-to-end
- [ ] `/v1/tokens/validate` confirms KV-cache hit on second call
- [ ] CHARTER.md, CHITTY.md, CLAUDE.md, AGENTS.md, SECURITY.md present and consistent
Expand All @@ -139,4 +143,4 @@ Documentation gate:
- [ ] No fake or seeded data in `schema.sql` (real shapes only)

---
*Charter Version: 1.2.0 | Last Updated: 2026-05-02*
*Charter Version: 1.3.0 | Last Updated: 2026-05-02*
14 changes: 11 additions & 3 deletions CHITTY.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# ChittyAuth App

> `chittycanon://core/services/chittyauth-app` | Tier 1 (Core Identity) | chittyauth-app.chitty.cc
> `chittycanon://core/services/chittyauth-app` | Tier 1 (Core Identity) | operator-chosen domain

## What It Does

Authentication application providing identity verification, token management, and session handling for all ChittyOS services.
Standalone authentication and API token provisioning. Issues, validates, refreshes, and revokes end-user Bearer tokens with HMAC-SHA256 signatures and SHA-256 hashed-at-rest storage. No ChittyOS shared-database dependency.

## How It Works

Cloudflare Worker deployed at chittyauth-app.chitty.cc.
Cloudflare Worker with two provider modes selected by `CHITTYAUTH_PROVIDER`:
- `local` (default) — D1 (`AUTH_DB`) + four KV namespaces (`AUTH_TOKENS`, `AUTH_REVOCATIONS`, `AUTH_RATE_LIMITS`, `AUTH_AUDIT`); validation hits KV first (30s cache), falls through to D1 on miss.
- `neon` — Neon OAuth facade; the worker fronts authorize/exchange endpoints and delegates issuance to Neon.

ChittyConnect integration is optional in either mode.

## Distinguished From `chittyauth`

Same tier, same function, different deployment: `chittyauth` (CHITTYFOUNDATION) shares identity data over Neon/`chittyos-core` for ecosystem services; `chittyauth-app` (CHITTYAPPS) is isolated D1+KV for third-party and custom deployments.
27 changes: 15 additions & 12 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ ChittyAuth App is a **token authority**. Any compromise of the signing key, the

| Boundary | Trust assumption |
|----------|------------------|
| `TOKEN_SIGNING_KEY` (Worker secret) | Confidentiality + integrity. Compromise = full forgery capability. |
| D1 `api_tokens` table | Integrity. Holds SHA-256 hashes only; plaintext recovery is infeasible from this table. |
| `CHITTYAUTH_ISSUED_MINT_API_KEY` (Worker secret; legacy alias `TOKEN_SIGNING_KEY`) | Confidentiality + integrity. Compromise = full forgery capability **once signature verification is enforced** (see Known Limitations). |
| D1 `tokens` table | Integrity. Holds SHA-256 hashes only; plaintext recovery is infeasible from this table. |
| `AUTH_TOKENS` KV (cache) | Soft state. Stale entries are bounded by 30s TTL; revocation must clear cache. |
| `AUTH_REVOCATIONS` KV | Authoritative for "revoked" decisions on the fast path. |
| Caller-provided tokens | Untrusted until verified end-to-end (signature, hash lookup, revocation check, expiry). |

## Cryptographic Design

- **Signing**: HMAC-SHA256 over canonical token payload, key from `TOKEN_SIGNING_KEY` (256-bit).
- **Signing**: HMAC-SHA256 over canonical token payload at issuance, key from `CHITTYAUTH_ISSUED_MINT_API_KEY` (legacy alias `TOKEN_SIGNING_KEY`, 256-bit). **Important caveat:** the validate path today does not re-derive or compare the embedded signature — it relies on the SHA-256 hash lookup against D1/KV. See Known Limitations.
- **At-rest storage**: SHA-256 hash of the issued token. Plaintext is **never persisted** server-side.
- **One-time disclosure**: Plaintext token is returned to the caller exactly once at issuance. There is no recovery path; lost tokens must be reissued.
- **Random sources**: Web Crypto `crypto.getRandomValues` / `crypto.randomUUID` only.
Expand All @@ -56,22 +56,23 @@ ChittyAuth App is a **token authority**. Any compromise of the signing key, the
A token is trusted only after all of:

1. Format check (prefix + base64url shape).
2. Signature verification with `TOKEN_SIGNING_KEY`.
3. SHA-256 hash lookup in D1 `api_tokens` (or KV cache for ≤30s).
4. `status === 'active'` and `expires_at > now`.
5. Not present in `AUTH_REVOCATIONS` KV.
6. Rate-limit check against `AUTH_RATE_LIMITS` KV (per-token, 1h window).
2. SHA-256 hash lookup in D1 `tokens` (or `AUTH_TOKENS` KV cache for ≤30s).
3. `revoked_at IS NULL` and `expires_at > now`.
4. Not present in `AUTH_REVOCATIONS` KV.
5. Rate-limit check against `AUTH_RATE_LIMITS` KV (per-token, 1h window).

Any step failing → reject; never short-circuit later checks.

**Signature verification is currently NOT part of the live validate path** (`src/token-manager.js` `validate()` hashes the bearer and looks up the hash; the embedded HMAC signature is generated at issuance but never re-derived on validate). Adding signature verification as step 2 — between format check and hash lookup — is tracked as a Known Limitation below; until that lands, an attacker who exfiltrates the D1 hash table does not gain forgery capability beyond replay of the already-hashed values, but the defense-in-depth that the signature is meant to provide is absent.

## Revocation Semantics

- Revocation writes to D1 (`status = 'revoked'`) **and** `AUTH_REVOCATIONS` KV **and** evicts `AUTH_TOKENS` cache entry.
- Code paths that consult only the cache without checking `AUTH_REVOCATIONS` are bugs and must be treated as security regressions.

## Audit

- D1 `audit_logs` records issuance, validation outcome, revocation, and refresh events.
- D1 `auth_events` records issuance, validation outcome, revocation, and refresh events (schema in `schema.sql`).
- Logs MUST NOT contain plaintext tokens, signing keys, or full bearer headers. Token references use the `tok_*` ID or hash prefix only.
- `AUTH_AUDIT` KV is a write-buffer — it is not the system of record; D1 is.

Expand All @@ -89,14 +90,16 @@ Any step failing → reject; never short-circuit later checks.
- [ ] `/health` returns `checks.database === true` and `checks.kv === true`
- [ ] No diff in this release introduces plaintext-token logging or new mocked auth paths
- [ ] Rate-limit window and TTL settings unchanged or reviewed
- [ ] Token-prefix scheme (`ca_live_`/`ca_test_`/`ca_dev_`) matches deploy environment
- [ ] Token-prefix scheme (`ca_live_`/`ca_test_`/`ca_dev_`, plus `svc_` for service tokens) matches deploy environment

## Known Limitations

1. **No application-level WAF or per-IP throttling** — relies on Cloudflare’s platform protections; per-token rate limiting is the only application-level throttle today.
2. **No automated CI security scanning configured in this repo** — CodeQL, secret scanning, and `npm audit` gates are not part of the workflow set in `.github/workflows/`. Reviewers must perform these checks manually until added.
3. **Single signing key, no kid rotation overlap** — rotating `TOKEN_SIGNING_KEY` invalidates all outstanding tokens. There is no dual-key verify window today.
4. **End-user tokens only** — service-to-service authentication is explicitly out of scope; do not retrofit `chittyauth-app` for inter-service auth.
3. **Signature verification is not enforced on the validate path** — `src/token-manager.js` `validate()` performs a SHA-256 hash lookup against D1/KV but never re-derives or compares the HMAC signature embedded in the token. The signing key is therefore advisory today, not load-bearing. Closing this gap is required before treating the signing key as the primary forgery defense; tracked as a follow-up issue.
4. **Signing-key fallback to a hardcoded development value** — `src/token-manager.js:11` falls back through `CHITTYAUTH_ISSUED_MINT_API_KEY` → `TOKEN_SIGNING_KEY` → a hardcoded `'dev-signing-key-change-in-production'` literal. A production deploy missing both secrets will silently use the dev key; deploys must fail closed instead. Tracked as a follow-up issue.
5. **Single signing key, no kid rotation overlap** — rotating `CHITTYAUTH_ISSUED_MINT_API_KEY` invalidates all outstanding tokens once signature verification is enforced. There is no dual-key verify window today.
6. **End-user tokens only** — service-to-service authentication is explicitly out of scope; do not retrofit `chittyauth-app` for inter-service auth.

## Security Contacts

Expand Down
Loading