Quoin docs

Introduction

Flags and experiments for teams with a database and no data team. Every number is safe to read the day you look at it.

Quoin is a feature-flag and A/B-experimentation product for small product teams. A team has one to ten engineers, a Postgres, SQLite or D1 database, and no data team to ask for help. Quoin is spun out of an in-house flags engine. That engine has run production experiments for a long time. So the statistics inside Quoin are not new. Only the packaging is new.

Built so far

Each item below is built and tested unless the item says otherwise.

  • QN-00a, QN-00b, QN-00c and QN-01 (sign in and create a workspace) are built and green. All 32 of 32 of the acceptance boxes of QN-01 are ticked, and three QA fix rounds landed. These items are real and tested: the workspace tables, the role matrix, the Better Auth sign-in, /login → /login/check-email → /workspaces/new, and every security item of QN-01. The security items are OTP lockout and replay, enumeration resistance, OAuth PKCE and state, session-cookie flags, CSRF, and rate limits. The auth tests run against a live Better Auth test instance, not a constructed session.

  • QN-02a (connect an app and see the first flag) is built end to end:

    • API keys (client, server and operator scopes, SHA-256 at rest, shown one time).
    • The flags/flagRows/flagRevisions tables, and verifyKey() behind both transports.
    • The SDK read at GET /api/flags, and the first-read beacon at POST /api/flags/first-read.
    • The apiKeys.* and flags.declare procedures, and createClient in @quoin/flags.
    • The connect screen (/connect, with the dev key shown one time).
    • The first-flag screen (/flags/:key). The connect screen redirects to it after the first read of the SDK arrives.

    See SDK.

  • QN-02b (record exposures) is built on top of QN-02a. It has the experiment_exposures and rollout_exposures tables, the rollout-exposure signal of the engine, and the exposure queue of the SDK (batched, retried, waitUntil-aware). It also has POST /api/exposures. See Exposures.

  • QN-11a (manage members and roles) is built end to end:

    • The four roles and their one shared permission check.
    • Invites (7 days, single-use, 20 per hour for each workspace).
    • Role changes with the last-owner guard.
    • Removal with its sign-out and CLI-key deletion.
    • The audit events for each action.

    See Members. The members screen (/settings/members), the invite-your-team step (/invite-team) and the accept-invite page (/invite/:token) are also built, on the same Settings shell.

  • QN-11b (manage keys and environments) is also built end to end:

    • apiKeys.update and apiKeys.revoke.
    • The operator-key self-only rule on apiKeys.create.
    • apiKeys.list, open to every role. It lists the environment keys of the workspace and your own operator keys.
    • The environments router (list/create/usage/delete). The three seeded environments cannot be deleted. The delete of a custom environment is an atomic sweep.
    • The createdBy column and five audit events. They include the apiKey.created event that was owed since QN-02a.

    See API. The environments screen (/settings/environments), the API keys screen (/settings/api-keys, the key shown one time behind a checkbox, revoke in one click) and the danger zone (/settings/danger-zone) are also built, on the same Settings shell, with two QA rounds landed.

  • QN-11c (audit log) is also built on the server side: the append-only audit_log table, every one of those writes, each of which puts its row in the same atomic batch, audit.list and audit.export, and the ten-table quoin-admin export. See Audit log. QN-11c-ii built the audit-log screen (/audit-log). It has the filter row (actor, object type, date window, key), the row detail with the note and the stored fields, the owner-gated Export CSV, and "Load older". So you can read in the product who changed what, and when.

  • QN-03a (roll out a flag) is also built on the server side:

    • The one-writer procedures flags.edit, flags.revert and flags.history, with the required note.
    • The guarded write that refuses a stale edit, the revision id and the audit row.
    • Operator keys that the shared role check accepts, and rate limits for each key.

    See Flags and rules and API. The quoin CLI and quoin login followed in QN-03a-ii. Every flags, experiments, guards and metrics command runs from a terminal. Every write shows what it will change and asks first. See CLI. GET /api/flags is served through a 30-second edge copy that Quoin purges on every flag write. Its speed targets wait for a first deployment, to be measured.

  • QN-03b (the read-only flag views) is built end to end:

    • flags.list and flags.get.
    • The flags list (/flags, grouped by what needs you, with its filter pills).
    • The flag detail (/flags/:key). It has the status sentence, the rule stack in evaluation order, the "Why does someone see it?" explainer evaluated in your browser, the revision history, and the definition-mismatch banner.
    • The environment switcher of the sidebar, as the one place where you choose an environment.

    See Flags and rules.

  • QN-03c (the rule editor) is built end to end on top of QN-03b. You declare a flag from /flags/new. You edit its rules at /flags/:key/edit. Every save has the diff of what would change, a required note and a revision id. The editor also has the stale-write re-plan when someone else saved first, rules frozen while an experiment runs, the Kill switch, the archive confirm with a 30-second Undo, and revert from the history panel. So a person does a rollout in the product, not as a tRPC request.

  • QN-05a (define a metric from events) is built on the server side. It has the metrics, events and identities tables, track() and identify() and their queue, POST /api/events, the metrics.* procedures, and the 13-month retention sweep. See Events and Metrics.

  • QN-05a-ii (the metric screens) is built end to end on top of QN-05a:

    • The metrics list (/metrics).
    • Declare and edit a metric at /metrics/new and /metrics/:key, from the events that you already send. The screens have "Save & test", the never-seen-event caution, and the frozen state after an experiment uses the metric.
    • The delete confirm.
    • "Where outcomes come from" (/connect/outcomes) in onboarding.

    See Metrics. The SQL source stays design stage.

  • QN-06 (plan, lock and start an experiment) is built end to end on top of QN-05a-ii and QN-03c. It has the experiments list (/experiments) and the wizard at /experiments/new. The wizard writes a hypothesis, picks a declared boolean flag, names a primary metric and guardrails, plans the weeks from your traffic, locks the pre-registration, and starts the experiment as one flag revision. See Experiments.

  • QN-07a (read an experiment report with its verdict and rulers, with QN-07b as its last build session) is built end to end on top of QN-06:

    • experiments.report, experiments.phases and experiments.stop.
    • The report page at /experiments/:key. It has the verdict sentence, the ruler, the six What-this-means states, the Details block, and the too-early, broken (sample-ratio) and clear-early branches.
    • The Stop dialog with its required reason.
    • The verdict and ruler cells on the experiments list.
    • The Home page at / with its needs-a-look list and right rail.

    You reload to see a newer report. Each read is one snapshot. Quoin draws nothing over time.

  • QN-08 (next steps with monthly impact) is built end to end on top of QN-07a. It has experiments.impact and the Next-step cards on every report state. A card shows a projection only when the verdict earns one: clear, significant-at-plan, or the budget of a guardrail under no-difference. Otherwise, a card shows a plain sentence. It also has the read-only rollout monitor at /rollouts with its staged ladder, guard and informational lift. See Experiments and Flags and rules.

  • QN-10 (conclude with a written decision) is built end to end on top of QN-08:

    • experiments.conclude as one atomic write of the decision record.
    • The end-experiment edit of the flag, with its revision and both audit rows.
    • The conclude dialog over the report at /experiments/:key/conclude, with its required note and the ship or roll-back choice.
    • The read-only concluded report with its stamp, crop marks and the decision record.

    See Experiments.

  • QN-04a (pause a flag automatically when a guard trips, and resume it) is built end to end on top of QN-10:

    • The hourly guard checks with a seven-day dry run for each guard.
    • The paused rollout page at /rollouts/:key.
    • The paused state of the flag page and its resume confirmation.
    • The Resume on Home.
    • Guard-pause email and webhook delivery, with the notifications settings at /settings/notifications.

    A person attaches a guard with quoin guards attach. But no one can turn automatic pausing on from the product yet. See Guards and Notifications.

  • QN-04a-ii polished those guard screens with no new capability. It changed the action row of Home, the chart labels and history of the rollout page, the dialog focus, and the Settings rail on a phone.

  • QN-09a-i-1 adds the AI analysis API on top of the report. It has experiments.analysis, the per-workspace setting, the monthly quota, the cache, and the refusal for reports that cannot be read yet. It is backend only, with no screen yet. See AI analysis.

  • QN-09a-i-2 adds the checks on what an analysis can say and the model service behind it. It has a versioned prompt and an adapter for the API of Anthropic. They are built and tested against a stand-in, with no key set and no model connected anywhere. It also has a per-minute cap and one composition for each analysis, however many people ask at the same time. It also has an eval of eight fixed reports and a set of hidden-instruction attacks that runs on every change. AI analysis stays off in production until someone checks it against the live API of Anthropic and the terms of the vendor are settled.

  • QN-05b (a metric from your own database) is built and tested but switched off. It has a per-unit SQL template that Quoin wraps and reads only as totals. It has a read-only connection that Quoin checks before it saves. It has the outcome source settings page and the database path on the connect and metric screens. It is on for a workspace only when an operator turns it on. It is never connected to a real customer database. See Database connections.

  • QN-05b-ii polished those screens with no new capability. It changed the layout of the metric editor, the connection field on a phone, the focus after you switch the outcome source, and the copy buttons, which now name what they copy.

  • QN-09a-ii adds the AI analysis block on top of that API. The block is on the report, on each rollout, and on a rollout that a guard paused. It has its own loading, cited numbers that you can open, the refusal, and the quota and rate-limit sentences. It has Refresh for editors and above. It has a setting to turn it on or off for the workspace, with a consent sentence that names Anthropic and what Quoin sends. It shows only while AI analysis is released. AI analysis is not released in production yet.

  • QN-09c adds "Useful?" with a thumbs up and down on a ready analysis. It allows one vote for each person for each read. Quoin keeps the vote for one purpose, and nothing reads it yet. QN-09c also adds "Investigate further" for editors and above. It asks a larger model about the same report and counts as 5 analyses. Each item is behind its own switch. Both stay off in production with the block. QN-09c also adds pnpm quoin ai backfill to pre-warm analyses after a prompt change.

  • QN-12 adds the limits of the Starter plan: up to 5 flags and one running experiment for each workspace. Quoin refuses more with a plan_limit message. Quoin counts tracked exposures each month on Settings → Billing. This page shows the plan and its usage with no card and no checkout. Past 2,000, the bar turns red and Home shows a banner. Quoin throttles nothing. See Plans and limits. QN-12 also adds a workspace switcher in the sidebar, quoin logout, and an operator command that erases the rows of one end user.

  • QN-12-ii adds the marketing site as its own app (apps/site). It has the landing page, pricing on the figures of the Starter plan only, and draft terms, privacy and sub-processor pages that are marked for legal review. The sign-in and invite pages link to them only after the origin of the site is configured. The site claims only what these pages say is built.

The hosted service runs at https://app.usequoin.app on Cloudflare Workers and D1. The SDK is on a private registry as @jingsu96/quoin-flags. The quoin CLI is not published yet. Underneath, these items are real, tested code: the flags engine (@quoin/flags), the data layer (@quoin/db), and the tRPC and plain-HTTP transports (/api/trpc, /api/auth/*, /api/admin/*, /api/flags, /api/exposures, /api/events). The own callout of each page says which of its claims this covers. docs/reports/qn-03a-ii.md, docs/reports/qn-05b-ii.md, docs/reports/qn-09c.md and docs/reports/qn-12-ii.md in the repo are the current state of play.

What makes it different

  • The sequential (always-valid) 95% interval is the headline number. You can look at a running experiment on any day and read the interval honestly. There is no "peeking" penalty. You do not wait for a fixed sample size before the number has a meaning.
  • Exposure is recorded only at the decision point. When you call decide() on a flag, Quoin records an exposure. isOn() and value() never record an exposure. This keeps your denominators honest and your sample ratio checks meaningful.
  • Health checks run before any number is shown. A sample ratio mismatch (p < 0.001) or fewer than 100 units in an arm stops the report from claiming a result. See Experiments for the six verdicts.
  • Flags are defined in code, rules live in Quoin. You declare the key, type, default, owner and expiry of a flag one time with defineFlags. You edit force rules, rollout percentages and experiment assignment in the app or the CLI. Quoin evaluates them first-match-wins.
  • Every write is a diff, a note and a revision. Nothing changes silently. Every edit to a rule shows what it replaces and requires a short note. You can undo it: quoin flags revert <key> --to r-0418 --env production.
  • No warehouse required. Quoin reads a metric from events that you send with track(), or from a SQL template that runs read-only against your own Postgres. You do not build a data pipeline first.

What Quoin will never show you

Some items are absent on purpose. They are not missing:

  • The confidence interval plotted over time. It invites a reader to peek at a curve, not at the number.
  • Per-user drill-down. Quoin reports on arms and cohorts, never on an individual.
  • A bare p-value as the headline. The interval and its verdict carry the meaning. A p-value alone does not.
  • "Chance to win". It is a probability statement that looks like certainty. Quoin states the figure and its interval.

Where to go next

Worked examples use Tessellate

Most examples on this site use Tessellate. Tessellate is a small sample SaaS workspace with three environments (production, staging, development) and the people Mia Okafor, Sam Reyes and Dev Patel. Every number in an example is a canonical figure. The examples use trial counts, lift percentages and confidence intervals. The specs and designs of the product use the same figures. They are not invented illustrations.

On this page