Suluk
    Preparing search index...

    Architecture

    CANDIDATE tooling for the OpenAPI v4.0 "Suluk" candidate (NOT official OAS, NOT SIG-ratified). This doc is the design spine for tooling/. The spec itself lives in specification/candidate-v4/.

    The user types the least possible contract surface:

    Hono endpoints  +  Zod validations  +  Better Auth settings
    

    …and the system derives the rest. The contracts are the single source of truth; every other artifact is a Derivation — a pure projection of the contracts. The OpenAPI v4 document is itself just one Derivation, never the source (this mirrors Suluk's own "the Document is a projection of the ledger" discipline).

       Data floor               Contracts                         Derivations (projections of the hub)
    ┌──────────────┐ ┌───────────────┐ ┌──────────────────────────────────────────────┐
    DrizzleCRUDHono routes │ │ v4 doc ─▶ 3.1 downgrade ─▶ Scalar / Swagger
    tables │ ─────▶ │ Zod schemas │ ─────▶ │ │ │
    │ (system of │ │ Better Authv4 │ ├──▶ Nano Stores client (state corner) │
    record) │ │ settingshub │ ├──▶ shadcn forms / tables (UI corner) │
    └──────────────┘ └───────────────┘ │ ├──▶ request-validation middleware
    │ ├──▶ auto-generated contract TESTS
    │ └──▶ documentation-coverage audit
    └──────────────────────────────────────────────┘

    ┌────────────┴─────────────┐
    suluk-vscode = the COCKPITevery layer above is visible, navigable,
    and actionable from one surface, re-projected per viewer + time.
    └──────────────────────────────────────────────────────────────────────┘

    The cycle is now declarative end to end: a Drizzle table is the system of record (@suluk/drizzle emits its CRUD RouteContracts); the contract projects to the v4 hub; and from the hub fall the docs (@suluk/scalar/swagger), the client state (@suluk/nano-stores), the UI (@suluk/shadcn), the validation, the tests, and the audit. The developer authors the data + contract; everything else is derived.

    The pivotal requirement: the emitted documentation must be dynamic — it varies by who and when is asking. So the central Derivation is not a static document but a pure function:

    render(contracts, principal, now) -> v4 Document
    
    • principal (the who): scopes / roles from Better Auth. The v4 by-name security model (C014) makes per-viewer filtering clean — include only operations whose security requirements the principal satisfies; redact or annotate the rest. A reader sees their API, plus their own facts (API keys, rate-limit budget) injected as examples / x- extensions.
    • now (the when): drives deprecation visibility, version selection, "what is live", scheduled rollouts. now is an input, never Date.now() read ambiently — so a render is reproducible and testable (you can ask "what did/will the doc look like at time T").

    A static export is just render(contracts, publicPrincipal, now). Nothing special-cases it.

    Package Role Status
    @suluk/core parse · validate(meta-schema) · resolve-by-name (own-property only) · signature · ADA · match ✅ 20 tests
    @suluk/openapi-compat v4 ⇄ 3.1 (the Scalar/Swagger lever; ingest external 3.x via upgrade) ✅ 12 tests
    @suluk/zod lossless Zod ⇄ v4 Schema Objects (fixpoint-proven) ✅ 29 tests
    @suluk/scalar / @suluk/swagger render a v4 doc via the 3.1 downgrade ✅ 5 + 6 tests
    @suluk/hono the derivation engine: render(routes, principal, now) -> v4; validation middleware; audit; contract-test generation ✅ 14 tests
    @suluk/better-auth official Better-Auth-on-Hono: auth settings → securitySchemes/security; ingest its openAPI() output via compat.upgrade; session → principal; the fail-closed previewLoginHandler (role-preview) ✅ 21 tests
    @suluk/drizzle data floor: Drizzle table → Zod (drizzle-zod) → v4 schemas + DB metadata + CRUD RouteContracts ✅ 17 tests
    @suluk/nano-stores state corner: v4 contracts → typed @nanostores/query fetcher/mutator stores (Zod-guarded I/O) ✅ 8 tests
    @suluk/shadcn UI corner: v4 Schema Objects → form/table specs + shadcn TSX (react-hook-form + zodResolver) ✅ 20 tests
    @suluk/builder the builder: DSL; buildApp/toShadcnRegistry; modules (C021) — installModule (refuse-on-collision + requires + dangling-ref backstop + namespacing), curated + remote + signed registries, gradeModule/previewInstall, provider slots (swapProvider), composition (planComposition/composeModules + stack templates — the non-dev "compose a platform" flow) ✅ 77 tests
    @suluk/deploy the capstone: swappable DeployProvider; Cloudflare (Workers + D1 + assets); a fail-closed preview variant (two locks + seed) ✅ 12 tests
    @suluk/cockpit the pure cockpit core (10-layer cycle · builder · codegen+diagrams · deploy · drift · cross-cut · converge · D2 diagrams · component pixel-confidence · ship-readiness gates (L3) · role-preview · validate/audit/preview) — shared brain ✅ 115 tests
    @suluk/admin the /superadmin web panel — the cockpit rendered as a Hono web UI, superadmin-gated ✅ 7 tests
    @suluk/example-petshop runnable end-to-end demo — real Drizzle → live CRUD + Scalar + frontend + /superadmin + client round-trip ✅ 10 tests
    @suluk/docs generate a GitHub-Pages docs site from source (Suluk documents itself); packageGraphD2 — the "how the tools compose" diagram on the Architecture page ✅ 14 tests
    @suluk/cost cost as a contract facet — declare per-op cost (x-suluk-cost, bubbles) + meter actual per-user cost ✅ 10 tests
    @suluk/stripe first-class Stripe behind a swappable PaymentProvider; bridges cost → metered billing ✅ 9 tests
    @suluk/visual pixel-confidence by construction — verify each primitive's pixels once; confidence propagates via content-hash ✅ 9 tests
    suluk-core (Rust) perf core: parse + signature + reverse-parse matcher; 2nd independent impl ✅ 9 tests
    suluk-vscode the cockpit's editor face — a thin vscode shell over @suluk/cockpit (Cycle + Builder TreeViews, Environments axis: connect/drift/live-cost, "View as", codegen, install module, ship-readiness checklist, preview as role, deploy) ✅ 6 tests (+ tsc + bundle)

    Total: 20 TS packages (432 tests) + a Rust core (9 tests) = 441 green. Shipped: npm (@suluk/*), crates.io (suluk-core), VS Code Marketplace (MahmoodKhalil.suluk-vscode). Live demo: a full SaaS (saasuluk) on Cloudflare Workers + D1 at saasuluk.saastemly.com — auth (Better Auth/D1), CRUD, Scalar docs, the /superadmin cockpit, a durable cost ledger, and a static Astro frontend.

    You shouldn't re-verify every pixel every time. Verify each UI primitive once (render it in isolation, screenshot it, approve it); the approval is recorded against the primitive's content hash. Thereafter any generated UI is pixel-confident without a new screenshot iff every primitive it uses is approved + unchanged — decided by hashing, not rendering. A primitive is re-verified only when its source changes (the hash drifts). Confidence propagates up the component → block → section → page tiers — the same verify-the-source- once discipline as the v4 ledger and the conformance grade.

    The cockpit's logic lives once, in @suluk/cockpit (pure: buildCycle, buildBuilderModel, codegen, deployPlan, validate/audit/preview — no host API). Two shells render it:

    • suluk-vscode — the editor face (TreeViews + commands + webviews).
    • @suluk/admin — the /superadmin web face (Hono-served HTML, superadmin-gated). Mounted with app.route("/", adminApp({ document, authorize })); the petshop demo mounts it live. Because both consume the same core, the admin panel is the extension — it cannot drift from it.
    1. Route registry. A thin wrapper that captures, per route: method, path, Zod schemas for param/query/header/json body and the response, plus optional metadata (summary, tags, security). Hono + @hono/zod-validator already declares the input schemas; we read them, so the contract stays in the route definition (minimal extra typing).
    2. emitV4(registry, ctx?) -> { document, diagnostics }. Zod→v4 via @suluk/zod; assemble the v4 Document. ctx = { principal?, now?, servers? } enables the dynamic projection (filter by scope, set servers by env, mark deprecations by now). No ctx ⇒ the full public doc.
    3. validator() middleware. Validate incoming requests against the same Zod schemas the doc is built from — the doc and the runtime can never drift, because they are one source.
    4. audit(document) -> Finding[]. Documentation-coverage: flag operations missing summary/description/response schema/examples. This is the ceiling side of the Conformance Grade (an under-documented route indicts the producer). Optional autofill synthesizes sane defaults.
    5. generateContractTests(document) -> TestCase[]. The doc as an executable check: per operation, assert valid example instances satisfy the request schema, invalid ones are rejected, the live response matches the declared response schema, and ADA signatures don't collide (reuse core.buildAda().collisions). Emit as bun:test cases.
    • Derive v4 securitySchemes (bearer / session-cookie / apiKey) + by-name security from the Better Auth instance config.
    • Ingest Better Auth's own openAPI() plugin output (OpenAPI 3.x for /sign-in, /session, OAuth callbacks, …) → compat.upgrade() → v4 → merge into the app doc, so the auth surface is documented without re-typing it.
    • Provide the principal extractor that feeds render(contracts, principal, now) — closing the loop on per-viewer docs.
    • @suluk/drizzle (data floor). A Drizzle table is the system of record. tableSchemas (drizzle-zod select/insert/update) → tableToV4 (via @suluk/zod) → tableComponents; tableMetadata reads pk/notNull/hasDefault/enum off the column descriptors; crudRoutes(table) emits list/get/create/update/ delete RouteContracts. emitV4(crudRoutes(table)) validates against the meta-schema — the floor reaches the hub with no hand-written contract.
    • @suluk/nano-stores (state corner). The same RouteContracts the server is built from project into a typed @nanostores/query client: GET → fetcher stores, the rest → mutator stores, with the contract's Zod guarding request and response edges (SchemaViolationError). One contract, two projections.
    • @suluk/shadcn (UI corner). v4 Schema Object → formSpec/tableSpec descriptors → shadcn TSX (renderFormTsx/renderTableTsx: react-hook-form + zodResolver). Codegen only — no runtime UI deps.

    The extension is where the whole cycle becomes coherent: one v4 document is the hub, and every projection is visible, navigable, and actionable from one place.

    • buildCycle(doc, {principal?}) (pure, tested). An 8-layer model — data · contract · auth · document · docs · state · ui · tests — each layer a projection of the hub. It is a function of the viewer: pass scopes and scope-gated operations the principal can't reach are filtered out (the per-WHO projection, applied at the hub; the hidden count surfaced honestly). docChecks = the doc as an executable check.
    • codegen (pure, tested). Actions that land files, each reusing a projection package: generateForm/ generateTable (shadcn TSX), generateStoresModule (the @suluk/nano-stores wiring + derived store list), exportV4Json. The editor never reimplements a projection.
    • The shell. A "Suluk · Cycle" TreeView with status icons; "View as…" re-projects the tree to a principal's scopes; commands for every codegen action; Scalar/Swagger webview previews; validate/audit diagnostics in the Problems panel.

    Inspired by ~/apps/multivendorbuilder's page → section → block → component DSL, rebuilt with the Suluk discipline. The load-bearing idea is contract-narrowing: a document's params is EXACTLY and ONLY what the tier above may set. Each tier consumes the full contract below, hardcodes most of it, and re-publishes a narrower params upward — the narrowing IS the safety surface (the same discipline as the per-viewer doc projection, applied to composition). "The owner can't change the form's fields" isn't a rule — fields just isn't in the section's params.

    The Suluk twist binds the tiers to the cycle: lower tiers auto-generate from an entity schema (Form/Table blocks, a CRUD section, the backend CRUD routes); a page composes sections. So buildApp(spec) emits both ends from one spec — backend (routes → emitV4 → v4 doc) and frontend (shadcn components + page TSX). Building a page builds the front and the back, because each entity carries data + contract + UI.

    shadcn's registry isn't UI-only: a registry:file item carries arbitrary files to any target. So toShadcnRegistry(builtApp) packages each slice as one installable unit (npx shadcn add <item>) bundling its shadcn Form/Table and its backend routes module and its v4 schema. The registry becomes the universal distribution format for every layer — managed from the cockpit. This is what makes the stack modular and installable end to end.

    The terminal stage: once a user logs into their Cloudflare account through the extension, everything deploys to Cloudflare — Hono → Workers, the frontend → Pages, Drizzle(sqlite) → D1, plus observability / compute. This is an adapter, not a rewrite: the stack is already Cloudflare-native by construction (Hono is the canonical Workers framework; @suluk/drizzle targets sqlite-core, which is D1). Kept behind a swappable target interface (@suluk/deploy) so Cloudflare is the first provider, not the only one — the services are built on good standards we can swap later. Built as @suluk/deploy: cloudflare.generate() emits wrangler.jsonc (D1 binding + static assets SPA-fallback + observability) + worker.ts (export default app) + schema.sql (from the entities) + an ordered plan (login → d1 create → apply → deploy). The extension's "Deploy to Cloudflare" command writes these + a DEPLOY.md and opens a terminal — it never runs wrangler for you (deploys are consequential; OAuth login happens in your terminal).

    Cost is just another facet of the contract. @suluk/cost lets an operation declare what it costs you (per-call + metered third-party usage, in raw µ$); annotateCosts writes it as x-suluk-cost on the operation, so it bubbles like everything else — through the 3.1 downgrade (the converter now passes x-* through), into Scalar, into the cockpit's Cost layer, and into a coverage audit (which operations declared nothing). At runtime, costMeter (Hono middleware) records what each request actually cost, attributed all the way down: the frontend action (@suluk/nano-stores tags requests with x-suluk-action) → the operation → each source. The ledger shows the raw per-user picture; you display it as it is and build pricing on top.

    @suluk/stripe is the reference PaymentProvider (swappable — other processors follow Stripe). It models the modern usage-based stack (Billing Meters + meter events + metered prices, customers, subscriptions, webhooks) as pure param-builders over a duck-typed client, and a bridge turns @suluk/cost events into Stripe usage — one meter event per principal. You meter usage; the price + your markup = revenue.

    • Honest losses are enumerated, never silentcompat collision diagnostics, zod lossy-effect warnings, hono audit findings, drizzle/shadcn warnings. The pattern is uniform.
    • Schema Objects are JSON Schema 2020-12 verbatim across v4 / 3.1 / Zod / Drizzle — no re-encoding, so the conversions are thin and the round-trips are exact.
    • One source, many projections. The data + contract are authored once; docs, state, UI, validation, tests, and audit are all derived — they cannot drift, because they are the same source.
    • Pure logic + thin adapters. Every package separates tested pure functions from a duck-typed/host shell (hono.mount, better-auth.mountAuth, the vscode shell). That is why coverage is high and the host bindings are trivial.
    • CANDIDATE labeling stays on every artifact; nothing here is official OAS.

    Each package derives one facet from the single v4 contract; here is how they depend on each other — every package pointing at its @suluk/* dependencies (rendered with d3).

    Suluk package dependency graph