next-expo-supabase-agent-harness

agent
Security Audit
Fail
Health Warn
  • License — License: Apache-2.0
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 8 GitHub stars
Code Fail
  • exec() — Shell command execution in .claude/hooks/posttool-factory-check.mjs
  • spawnSync — Synchronous process spawning in .claude/hooks/posttool-factory-check.mjs
  • process.env — Environment variable access in .claude/hooks/posttool-factory-check.mjs
  • rm -rf — Recursive force deletion command in .claude/hooks/pretool-bash-guard.mjs
  • process.env — Environment variable access in .claude/hooks/pretool-write-guard.mjs
  • spawnSync — Synchronous process spawning in .claude/hooks/stop-factory-gate.mjs
  • process.env — Environment variable access in .claude/hooks/stop-factory-gate.mjs
  • rm -rf — Recursive force deletion command in .claude/settings.json
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

A CLI that scaffolds a Next.js and Expo (React Native) monorepo on Supabase, with Claude Code hooks and CI gates that block an agent turn or a merge until validation passes.

README.md

next-expo-supabase-agent-harness

OpenSSF Baseline
OpenSSF Scorecard
npm version

A CLI that scaffolds a pnpm monorepo with a Next.js 16 web app and an Expo 57
(React Native) mobile app on one Supabase backend, plus Claude Code hooks and CI
gates that block an agent turn or a merge until validation passes.

A terminal recording: init writes a project, the Bash guard denies a force-push, the secrets gate goes red on a service-role key in a tracked file and green once it is removed

No model runs in the recording. The hooks are programs that read the JSON payload Claude
Code hands them, and the gates are programs that exit 1, so what they print is what the
agent is shown. It is generated from
docs/media/harness-demo.tape, and a test runs that script's
commands against each change, so a message that changes reds until the recording is
regenerated.

The npm package is this CLI. npx downloads it and runs it in one step, from an empty
folder for the new project:

npx --yes next-expo-supabase-agent-harness@latest init

Or install it globally once:

npm i -g next-expo-supabase-agent-harness

and then, from an empty folder for the new project, run:

next-expo-supabase-agent-harness init

Either way, init asks for the project's details before it writes anything: its name, its
package name, the app identifier, the Supabase project ref, the GitHub owner and a few more.
Each question shows a default in brackets that Enter accepts, and init --yes accepts them
all without asking. The scaffold is written into the current folder.

Install with -g: a local npm i writes a package-lock.json into the folder, and init
stops when it finds one. A global install stays at the version you installed, so run
npm i -g next-expo-supabase-agent-harness@latest before update to move it to the newest
release; update refuses to run from a CLI older than the project.

To pin a release, take a version number from the
Releases
page (the tag without its v) and use it in place of latest, or install
next-expo-supabase-agent-harness@<version>. Once init has finished, in the same folder:

git init          # first: the prepare script (lefthook install) needs a repository
pnpm install
git add -A && git commit -m "chore: scaffold"   # the first commit must include pnpm-lock.yaml
pnpm validate

Status: stable (2.0.x). CI proves the scaffold on Linux, and on macOS without
the database gates. The installer's unit tests run on Linux, Windows and macOS.
pnpm validate has never run in CI on Windows. See Limitations.

Who this is for

The harness assumes one shape: a pnpm monorepo with a Next.js web app and an Expo mobile
app on one Supabase backend, Node 22 or newer, Docker for the local database the database
gates run against, and Claude Code as the agent, because Claude Code is what runs the
hooks that block a turn. Enforcement comes in layers: the Claude Code hooks, the lefthook
commit hooks, pnpm validate, and CI. Outside that shape, some layers still apply:

  • A web-only app. Not a fit today. init writes both apps, and the mobile gates
    judge apps/mobile. Without it, expo-policy and native-deps skip on a laptop but
    fail closed in CI, and parity goes red on the mobile paths its register names. CI
    runs the frozen step list, so a project cannot drop those gates.
  • An existing monorepo with its own CI. init retrofits a pnpm workspace that has
    apps/web or apps/mobile. It keeps your configs and parks the harness's beside them
    as <name>.harness.<ext> files, and gate-integrity stays red until each one is merged
    or accepted after review. It keeps a workflow file you already have, so your own
    ci.yml runs no harness gate until you add the chain to it, and an existing
    lefthook.yml is one of the parked configs. The Claude Code hooks are merged into your
    .claude/settings.json. A repository on npm, yarn or bun, or without
    pnpm-workspace.yaml, is refused.
  • An agent that is not Claude Code. The commit hooks, pnpm validate and CI apply as
    they do for a person. The .claude/ hooks never run, so nothing denies a command before
    it runs or keeps a turn from ending on a red tree: a violation is caught at the commit
    or at the pull request instead.

Requirements

  • Node 22 or newer
  • pnpm 11 (corepack enable picks up the pinned version)
  • Docker, for the local Supabase stack that the database gates run against
  • Optional: the gitleaks binary. The pre-commit secrets scan prints a skip
    notice without it.

What you get

apps/web        Next.js 16 App Router. Serves the tRPC API for both clients.
apps/mobile     Expo 57, expo-router, React Native 0.86.
packages/api            framework-neutral tRPC v11 router
packages/contracts      zod DTOs shared by both apps
packages/platform/*     env, errors, events, observability, ratelimit, supabase
packages/verticals/*   your features (empty until you add one, or init --with-demo)
packages/design-tokens, design-system, design-system-native
supabase/       SQL-first schema, migrations, RLS policies, pgTAP tests, one Edge Function
tools/, .claude/, .github/   the gates, hooks and workflows described below

A default init writes no example feature. init --with-demo adds one worked
feature end to end on both apps (the @app/notes vertical, its screens, routes
and migrations, and its rows in the registers), and eject takes it back out;
see docs/cli.md.

Postgres row-level security keyed on auth.uid() is the one authorization
boundary for both apps. Versions are pinned in a single pnpm catalog
(pnpm-workspace.yaml): Next 16.4, Expo 57, React 19.2, TypeScript 6, Biome 2.6,
supabase-js 2, tRPC 11, zod 4.

How it works

  • PreToolUse guards. Hooks check every shell command, file write and MCP
    call against a data table of 151 guard-rule ids before the agent's tool runs.
    Nine hooks are wired: seven guards, a launcher that fails closed if a hook
    cannot load, and a session-start brief.
  • Stop hook. The agent cannot end a turn until pnpm validate, the RLS
    isolation tests against a real Postgres, both unit suites with coverage,
    per-file diff coverage, duplication, i18n, test quality, the mobile perf
    closure and the reviewer verdicts all pass.
  • pnpm validate. One chain of 37 gates, ordered cheap to expensive, driven
    by tools/harness.config.mjs. Each gate is documented with a proof that it
    can fail in the gates catalog.
  • CI. The same chain runs against a frozen copy of the step list
    (tools/validate.floor.json), so a project can add gates but cannot quietly
    remove one. Gates that need a database or a toolchain skip loudly on a
    laptop and fail closed in CI.
  • Supply chain. Every workflow the harness ships pins its actions by commit
    SHA, and zizmor audits them. OSV-Scanner checks the lockfile for known
    vulnerabilities, gitleaks scans for secrets, and the Renovate config lets a
    new npm release age (minimumReleaseAge) before proposing it. The
    ci-provenance module adds an SBOM and build provenance attestations.
  • Commit hooks. lefthook runs Biome and gitleaks before a commit and
    commitlint on the message.

The design is described in the
harness doctrine, which ships into every
scaffold. Its
hook map
lists each hook, the event it runs on and what it enforces.

Compared with other starters

Other projects also put a Next.js app and an Expo app in one repository:

  • create-t3-turbo is a Turborepo
    starter with an Expo app, Next.js and TanStack Start web apps (you pick one),
    tRPC, Better Auth, and Drizzle preconfigured for a Supabase Postgres
    database. You start from a copy of it, made with GitHub's "Use this template"
    button or with
    npx create-turbo@latest -e https://github.com/t3-oss/create-t3-turbo.
  • Solito is a library that unifies
    navigation between React Native and Next.js. npx create-solito-app@latest
    writes one of its example monorepos; in the default one, both apps render the
    same screens from a shared package.

In this harness:

  • Authorization is Postgres row-level security keyed on auth.uid(). Both apps
    meet the same policies, and pgTAP and supabase-js suites test them against a
    real database.
  • Claude Code hooks and CI gates block an agent turn or a merge until
    validation passes.
  • update applies a later harness release to a project that already exists.
  • The two apps share the API, the contracts and the design tokens, not their
    screens.

Commands

Command What it does
init Scaffold into --dir (default .). --tier core|standard|strict, --modules a,b, --set VAR=value, --dry-run.
update Apply a newer harness version to an existing install. --rollback restores the tree recorded before the last update.
doctor Report whether an install is healthy.
enable <module> / disable <module> Add or remove an opt-in module.
graduate Advance the install's base version once ramped checks are clean.
apply-proposal [<id>] Apply, as a human in a terminal, a register edit an agent staged in harness-proposals/: it shows the reason and the diff and writes the file once you type its path. No id lists them. --dry-run.

Full flag and placeholder reference: docs/cli.md, or --help.

Tiers choose which opt-in modules are installed. core installs none.
standard (the default) installs ci-provenance, ci-mobile-release and
ci-web-deploy. strict installs all twelve: those three plus device-e2e,
eas-update, store-metadata, gate-a11y-deep, crash-reporting,
push-notifications, eval-live, observability and e2ee. The Vercel deploy
workflow, EAS Build/Submit and release-please come from modules, not the base.

Claude Code plugin

The agents, slash commands and skills also come as a Claude Code plugin, for a session
outside a harness-installed project. Install it from this repository's marketplace:

claude plugin marketplace add cogvera-labs/next-expo-supabase-agent-harness
claude plugin install next-expo-supabase-agent-harness@next-expo-supabase-agent-harness

or, inside a session on Claude Code 2.1.275 or later:

/plugin install next-expo-supabase-agent-harness --marketplace cogvera-labs/next-expo-supabase-agent-harness

The plugin carries no hooks, gates or permissions; those come with init. Inside a
harness-installed project you do not need it, and its reviewers' verdicts do not count
toward the reviewer ledger. See plugin/README.md.

Limitations

  • The database gates are proven on Linux only. Every CI lane with a
    Supabase stack runs on ubuntu-latest. The macos-scaffold lane runs
    init and pnpm validate on a Mac without Docker, the way a laptop runs
    them, and fails if any gate skips except types-drift, which needs the
    database. No lane runs a scaffold on Windows.
  • Rate limiting covers the application layer only. It binds the tRPC router
    and Next.js Server Actions. It does not bound direct PostgREST calls made
    with a user's JWT, or sign-in and sign-up, which go to Supabase Auth. If the
    rate-limit backend is unreachable, it degrades to an in-process limiter, and
    it fails open if that also fails.
  • The e2ee module ships building blocks, not an encrypted app. No shipped
    feature is encrypted; the worked example init --with-demo adds stores
    plaintext. The module's own README lists what it does not solve.
  • Device tests are not per-PR. The Android emulator and Maestro lane runs on
    a schedule and on manual dispatch.
  • The guards are tamper-evident, not tamper-proof. See the scope notes in
    SECURITY.md.

The longer list is kept per release under "What stays open" in the
CHANGELOG.

Measured cost

Recorded by CI on a GitHub-hosted Linux x64 runner on 2026-10-02 and committed
in scripts/chain-budget.json. They are that runner's numbers, not a promise
about your machine.

Run Wall time
pnpm validate, warm warm ≈ 27 s (26881 ms)
pnpm validate, cold cold ≈ 145 s (145363 ms)
Stop hook, full turn end 54.8 s (54815 ms)

Compliance mappings

The scaffold ships two registers that map published requirements to the gate,
hook or test that addresses each one, including the requirements it does not
meet. These are mappings. They are not certifications, and the project claims
no conformance level. The gates print the current standing as:

149 ML3 requirements: 8 effective, 13 alternate-control, 11 not-implemented, 61 not-applicable, 56 organisation-boundary; 8 shared clauses
392 mapped requirements: 11 covered, 149 partial, 105 not-covered, 127 not-applicable — ASVS 5.0.0 (345), MASVS 2.1 (24), CRA Annex I (23)

The first line counts the requirements of ASD's Essential Eight
(register). The second
counts the requirements of three further standards
(crosswalk).

Documentation

Contributing and feedback

To fork the harness itself into a sibling for a different stack, start from a
clone with a fresh history, as docs/forking.md describes.
This repository is descended from
expo-postgres-agent-harness
and
tauri-postgres-agent-harness.

License

Apache-2.0 for the repository. Everything under template/** is
"Apache-2.0 OR 0BSD": recipients choose either, so scaffolded code carries no
attribution requirement.

Reviews (0)

No results found