{"protocol":"clank-doc/1","frameworkVersion":"0.8.0","slug":"code-audit","title":"Code and product audit","description":"This document records what was inspected, what changed, and what remains intentionally out of scope. It is evidence for maintainers, not a claim that any framework can make every application correct automatically.","group":{"id":"security","title":"Security and resilience"},"url":"https://docs.clank.run/docs/code-audit","source":"docs/code-audit.md","headings":["Scope","Findings resolved","Readability decisions","Security posture","Application coverage","Known limits","Release gate"],"tableOfContents":[{"id":"scope","title":"Scope","level":2},{"id":"findings-resolved","title":"Findings resolved","level":2},{"id":"readability-decisions","title":"Readability decisions","level":2},{"id":"security-posture","title":"Security posture","level":2},{"id":"application-coverage","title":"Application coverage","level":2},{"id":"known-limits","title":"Known limits","level":2},{"id":"release-gate","title":"Release gate","level":2}],"markdown":"# Code and product audit\n\nAudit date: 2026-07-25\n\nThis document records what was inspected, what changed, and what remains intentionally out of scope. It is evidence for maintainers, not a claim that any framework can make every application correct automatically.\n\n## Scope\n\nThe audit covered:\n\n- reactive ownership, computed invalidation, DOM updates, SSR, and hydration;\n- TSX public typing and event/control ergonomics;\n- runtime schemas and agent contracts;\n- form state, validation, submission, focus, and cancellation;\n- accessible disclosure, dialog, tabs, pagination, and directives;\n- semantic agent inspection and operation;\n- auth, passkeys, organization RBAC, data ownership, requests, files, deployment artifacts, migrations, secrets, ingress, backup, and release supervision;\n- examples, strict types, package contents, documentation, desktop rendering, and narrow viewports.\n\n## Findings resolved\n\n| Finding | Resolution | Evidence |\n| --- | --- | --- |\n| A computed first read through `peek()` did not retain its own dependencies | `Computed.peek()` now suppresses only the caller subscription while evaluating with its own observer | Core regression test |\n| Boolean ARIA `false` was removed | DOM and SSR now serialize explicit `\"true\"`/`\"false\"` states | DOM and SSR tests |\n| Forms required repeated ad hoc signal/error code | Added schema-aware headless forms with cancellation, focus, server errors, reset, manifests, and typed controls | Form tests and three site variants |\n| Agent inspection relied heavily on custom labels | Native IDs, labels, roles, required/readonly/invalid/checked/multiple state, and placeholders are understood | Agent-surface tests |\n| Semantic inspection could expose password input values | Password and file values are omitted; file input automation is refused | Agent-surface tests |\n| Reusable interactive controls were application-specific | Added disclosure, modal dialog, tabs, pagination, outside-click, and autofocus primitives | UI tests and site variants |\n| Common web schemas were verbose | Added email, URL, date, date-time, record, defaults, refinement, and numeric/boolean coercion | Schema tests |\n| JSX intrinsic elements were effectively `any` | Added native element/property/event typing, reactive attributes, bind/ref/directive types, ARIA/data attributes, and custom-element support | Strict type tests |\n| Existing examples covered mostly todos | Added commerce, SaaS dashboard, and multi-step booking applications | Browser verification |\n| Concurrent build/watch output could briefly disappear | Framework and public compiler builds now replace files atomically and remove only stale outputs | Concurrent-build regression tests |\n| Documented clean example URLs returned 404 | The development server now resolves trailing-slash directories to `index.html` | Browser and HTTP verification |\n| Generated apps did not declare their runtime for local development and editor types | Scaffolds now depend only on their matching Clank release and include build, dev, start, and deploy scripts | Package-consumer scaffold verification |\n| A refreshed authenticated deployment page still displayed the `Sign in` heading | The server-rendered and client-rendered auth card now derive their heading from the same session state | Platform regression test and browser refresh verification |\n| The Proact name remained embedded across package, CLI, storage, protocol, and UI surfaces | Renamed the product to Clank with in-place data migration and narrowly scoped legacy readers | Rename compatibility tests and migrated production-state copy |\n| Authentication lacked production recovery and phishing-resistant credentials | Added email verification, generic single-use recovery, bounded MFA, WebAuthn passkeys, and atomic counter advancement | Auth and synthetic WebAuthn tests |\n| Project authority was account-wide | Added organizations, invitations, four roles, scoped tokens, permission intersection, and removal-time revocation | Platform RBAC and isolation tests |\n| Deploy coordination was local-only | Added durable authenticated leases, fences, nodes, desired generations, idempotent operations, retry, and stale-worker rejection | Orchestration and chaos tests |\n| Backups were release-local snapshots or manual recovery points only | Added encrypted authenticated backup repositories, durable scheduling, cross-control-plane claims, retention, verification, restore confirmation, safety copies, path-safe API/console surfaces, and CLI | Recovery, platform-backup, conformance, and chaos tests |\n| The platform lacked a managed host/data-plane layer | Added exact-host ingress, DNS ownership challenges, external PostgreSQL transactions/migrations, and database provisioning contracts | Data-plane and platform tests |\n| Release security evidence was manual | Added ASVS-oriented mapping, threat model, package/secret audit, immutable CI actions, CodeQL, chaos tests, and beta gate | `npm run check` and GitHub workflows |\n| Hosted quotas could be bypassed through extra organizations and rejected domains could survive rollback | Added account organization/site limits and moved domain capacity checks into the insert transaction | Platform quota and direct SQLite row-count regressions |\n| Caller IP headers, passkey start responses, and recovery delivery timing exposed authentication side channels | Bound rate limits to trusted adapter identity, switched to discoverable passkeys, and removed delivery from the response path | Authentication enumeration, spoofing, and blocked-delivery regressions |\n| Binary uploads and remote SQL responses could buffer past route limits | Added shared bounded streaming readers that cancel at the first over-limit chunk | Artifact, file-service, and PostgreSQL streaming regressions |\n| Disconnects did not cancel every downstream stream and Docker arguments contained secret values | Propagated cancellation through ingress/Node and changed Docker to name-only environment arguments | Ingress, Node disconnect, and fake-Docker process-argument regressions |\n| Repository leak checks covered only the package and a small credential set | Expanded credential patterns and scan current repository files plus all reachable Git history | `npm run security:audit` |\n| A green release did not enforce coverage or documentation/declaration integrity | Added minimum line/branch/function coverage and checked local links, guide indexing, declaration parity, and export targets | `npm run check` and `npm run docs:audit` |\n| CLI state writes and control-plane responses lacked complete interruption/resource bounds | Added strict bounded config/link parsing, atomic private replacement, request deadlines, strict UTF-8 JSON parsing, and response byte caps | CLI regression tests and packaged conformance |\n| `/healthz` reported a constant result and signal shutdown could skip platform cleanup after an HTTP close failure | Added storage-backed readiness, separate liveness, and bounded cleanup that attempts both layers | Platform readiness tests and conformance process shutdown |\n| Repeated deployments retained unbounded extracted files and rollback snapshots | Added locked per-project count/byte ceilings plus path-derived, rollback-scoped cleanup that preserves immutable evidence | Platform quota, authorization, symlink, snapshot, CLI, and dashboard regressions |\n| Enforced site quotas had no safe user-facing reclamation path | Added owner/admin-only permanent deletion with dual confirmation, scoped-token denial, durable locking, path-safe storage cleanup, metadata cascades, token revocation, and surviving audit evidence | Platform RBAC, CSRF, path substitution, runtime, storage, orchestration, quota/slug/port/domain reuse, CLI, and dashboard regressions |\n| Deleted-site audit evidence survived in SQLite but became unreachable through the project API | Added durable organization attribution, old-schema backfill, current-role filtering, project-token containment, stable cursor pagination, dashboard Activity, and structured CLI output | Deletion survival, migration, RBAC/scope, pagination/input, CLI, desktop/mobile, refresh, and browser-console regressions |\n| Routine workspace access administration and invitation acceptance required CLI work, while replacement invitations remained valid | Added complete browser invite/accept/member administration, CLI parity, administrator-only pending metadata, developer audit redaction, atomic invitation replacement, active-invite bounds, revocation, and existing-member rejection | RBAC, replay, email binding/privacy, cap, role/removal, CLI, CSRF, desktop/mobile, and browser-console regressions |\n| Creating an additional workspace still required CLI knowledge | Added quota-aware browser creation, normalized slug preview, immediate selection, and server-authoritative validation | CSRF/quota regression, authenticated browser creation, and responsive dialog verification |\n| Default bootstrap made invitations unusable for collaborators who did not already have an account; simultaneous control planes lacked a durable first-account claim; and platform/auth path normalization differed | Added email-bound invitation-assisted registration with rollback cleanup, an expiring SQLite bootstrap claim with insertion-order fallback, and one normalized signup-policy operation | Disabled/bootstrap, repeated-slash/legacy path, origin, expiry, mismatch, replay, account-cleanup, and two-runtime concurrency regressions |\n| Reusing the console after sign-out could retain the prior account's rendered People data and stale bootstrap button state | Reload the server-rendered console on sign-out or session expiry so identity, policy, and DOM state are rebuilt together | Same-browser owner-to-invitee onboarding, refresh, role/privacy, and console-error verification |\n| Platform auth and device-start throttles reset per process, while successful login cleared a malformed key | Added one atomic HMAC-keyed SQLite sliding-window store shared by auth and device onboarding, corrected successful-login clearing, and bounded high-cardinality retention | Framework reset, cross-runtime accumulation, restart persistence, raw-identity absence, clock rollback, 429, and 20k-key pruning regressions |\n| Generated auth pages rendered correctly but mapped `clank` while compiled browser modules imported the framework package, preventing hydration | Aligned both scaffold import maps with `@clank.run/framework` and added generated-source regression coverage | Fresh scaffold registration and two-browser live-sync verification |\n| CLI help could execute a command, offline dry-runs required auth, unknown options were ignored, and async failures had inconsistent exit behavior | Added command-aware help, strict option validation and suggestions, reliable async error handling, offline dry-runs, readiness diagnostics, and structured agent output | CLI regression tests, local-checkout consumer, packaged conformance, and fresh-app browser review |\n\n## Readability decisions\n\n- New behavior is split into focused `forms.ts` and `ui.ts` modules.\n- Public controllers are headless and return ordinary props.\n- Runtime schemas remain the source for TypeScript and agent contracts.\n- IDs are deterministic for SSR, hydration, accessibility, and agent operation.\n- Unsafe or unknown form keys throw rather than fail silently.\n- Examples use domain-specific names and semantic native HTML.\n\nLarge existing modules such as `backend.ts`, `auth.ts`, and `platform.ts` remain cohesive but substantial. Splitting them without changing their public boundaries is future maintainability work; a mechanical split was not treated as inherently safer than tested cohesive code.\n\n## Security posture\n\nThe existing security boundaries remain:\n\n- bounded request and artifact intake;\n- executable URL and inline-handler rejection;\n- safe SSR escaping and serialized state;\n- scrypt password hashing, CSRF, secure cookies, rate limits, roles, and revocation;\n- owned SQLite rows and auth-partitioned live queries;\n- traversal/symlink defenses;\n- encrypted deployment secrets;\n- immutable migration history, backup, health-gated activation, and rollback.\n\nThe audit added semantic password/file redaction and stricter form-key handling. Client forms and hidden UI are never authorization boundaries.\n\nThe 0.7.0 rename also preserves existing accounts, sessions, application rows, migration history, projects, releases, secrets, logs, and audit records. Clank writes only the new names after migration. See [Renaming from Proact](renaming-from-proact.md).\n\n## Application coverage\n\nThe current examples prove different mechanics:\n\n- `hello`: reactive primitives and agent actions.\n- `todo`: keyed client CRUD.\n- `fullstack`: SSR, SQLite, RPC, and live synchronization.\n- `auth-todo`: sessions, user-owned data, SSR, and multi-tab live updates.\n- `commerce`: search/filter/sort, cart state, modal checkout, validation, and async confirmation.\n- `dashboard`: tabs, metrics, responsive navigation, tables, filtering, pagination, invite dialog, and settings.\n- `booking`: multi-step composition, cross-field dates, room selection, computed pricing, guest validation, and confirmation.\n\nThese examples demonstrate framework breadth. They do not replace domain-specific payment, tax, inventory, medical, legal, or regulatory integrations.\n\n## Known limits\n\n- Form paths are intentionally top-level. Compose controllers for nested editors and independent wizard steps.\n- A capability-gated local file store and upload endpoint are included for trusted single-host deployments; production object storage, CDN delivery, and image transformation remain provider integrations.\n- No virtualized list is included yet; large datasets should page server-side.\n- Dialogs are rendered in place rather than through a portal.\n- The built-in process supervisor remains single-leader even though durable distributed coordination primitives are available.\n- The trusted process runner is not a sandbox; use the Docker runner for stronger isolation.\n- Clank now provides verified domain eligibility for Caddy On-Demand TLS, but certificate/key custody, WAF/DDoS service, WebSocket ingress, remote worker integration, and globally distributed control storage remain external or future platform work.\n- Tailwind's browser build is suitable for examples and zero-install prototyping; production applications should serve compiled CSS.\n\n## Release gate\n\nA release is acceptable only after:\n\n1. the zero-dependency TS/TSX syntax-lowering build succeeds;\n2. checked-in declarations match built declarations, package export targets resolve, and local documentation links remain valid;\n3. all unit and end-to-end tests pass above the enforced 80% line, 65% branch, and 80% function coverage floors;\n4. package contents contain no databases, environment files, credentials, or platform state;\n5. fresh package consumers can scaffold and build;\n6. representative applications pass browser interaction, console/error, accessibility-tree, and responsive-layout checks;\n7. `npm run conformance` passes against a packed release through browser auth, CLI device authorization, live synchronization, user isolation, deployment, migration, failed activation, rollback, and data restoration;\n8. `npm run security:audit` verifies dependency, package-content, current-tree and reachable-history credential patterns, governance, least-privilege, immutable-action, OIDC, and evidence requirements; and\n9. deterministic chaos tests prove worker reclaim/fencing, corrupt-backup fail-closed behavior, and ingress recovery.\n\nClank deliberately does not install a TypeScript package. Its built-in compiler validates syntax lowering, while the checked-in declarations define the consumer contract. Run `tsc --noEmit` as an additional semantic type check when a separately provisioned, trusted TypeScript compiler is available; do not describe that optional external tool as part of the zero-dependency gate.\n\nSee `docs/security.md` and `docs/platform-security.md` for the separate security checklists.\n"}