Hatch Developer Guide
For contributors, integrators running the stack locally, and anyone building on the Hatch API.
Complements: ARCHITECTURE.md (how the pieces fit), API.md (REST reference), CONTRIBUTING.md (PR workflow + commit convention).
Table of contents
- Prerequisites
- First-time setup
- Environment variables
- Running the stack
- Tests
- Database — Drizzle + migrations
- Contracts — Foundry
- Common workflows
- Troubleshooting
- Contributing
Prerequisites
| Tool | Version | Install |
|---|---|---|
| Node | 20.11+ | nvm / volta / asdf |
| pnpm | 9.12+ | corepack enable |
| Foundry | 2024.05+ | curl -L https://foundry.paradigm.xyz | bash && foundryup |
| Postgres | 15+ | local Docker or Supabase |
| Docker | optional | for the local PG container |
macOS tip: if
corepack enablefails with a permissions error,sudo corepack enableor run via a Node version manager that doesn't require sudo.
First-time setup
git clone https://github.com/rajkaria/hatch.git
cd hatch
corepack enable
pnpm install
cp .env.example .env
# Edit .env — at minimum fill DATABASE_URL and ANTHROPIC_API_KEY to enable
# the scoring pipeline. See "Environment variables" below.
# Bootstrap the database
pnpm --filter @hatch/api db:migrate
pnpm dev # starts web (3000) + api (3001)
open http://localhost:3000
Optional — local Postgres via Docker
docker run -d --name hatch-pg \
-e POSTGRES_PASSWORD=hatch \
-e POSTGRES_DB=hatch \
-p 5432:5432 \
postgres:16
# then in .env
DATABASE_URL=postgres://postgres:hatch@localhost:5432/hatch
Environment variables
Every variable documented in .env.example. The ones you actually need
for each local mode:
Minimum to run web (no scoring)
NEXT_PUBLIC_ORIGIN=http://localhost:3000
HATCH_API_BASE_URL=http://localhost:3001
Minimum to run api with scoring
DATABASE_URL=postgres://…
ANTHROPIC_API_KEY=sk-ant-…
ANTHROPIC_MODEL=claude-sonnet-4-5-20250929 # default
PORT=3001
Optional — unlock specific features
| Variable | Unlocks |
|---|---|
BSC_RPC_URL + HATCH_ATTEST_ADDRESS + ATTESTER_PRIVATE_KEY |
On-chain attestation publishing |
BITQUERY_API_KEY |
Real creator signal (Sprint C.2.2) |
GOPLUS_API_KEY |
Real risk signal (Sprint C.2.3) |
WEBHOOK_ENCRYPTION_KEY |
Webhook service (H.4) |
ADMIN_TOKEN |
Admin panel + analytics + status |
ENROLLMENT_DOMAIN |
EIP-191 enrollment signatures (creator onboarding) |
.env is gitignored. Never commit secrets. Production env lives in
Vercel / Railway / Supabase dashboards — see
deployment guide.
Running the stack
| Target | Command | URL |
|---|---|---|
| All apps | pnpm dev |
http://localhost:3000 |
| Web only | pnpm --filter @hatch/web dev |
http://localhost:3000 |
| API only | pnpm --filter @hatch/api dev |
http://localhost:3001 |
| Swagger UI | open /api/v1/docs on the api |
http://localhost:3001/api/v1/docs |
| Drizzle Studio | pnpm --filter @hatch/api db:studio |
browser-based |
Health checks
GET /health— simple liveness ping.GET /ready— readiness (DB ping, breaker state).GET /v1/admin/status— full health probe (bearer-gated).
Tests
# Unit + integration (Vitest) — every package that has tests
pnpm test
# Single package
pnpm --filter @hatch/api test
pnpm --filter @hatch/web test
# Single file
pnpm --filter @hatch/api exec vitest run src/modules/admin/routes.test.ts
# Playwright E2E (web)
pnpm --filter @hatch/web test:e2e
# Contracts
cd packages/contracts
forge test -vvv
forge coverage --report summary
Test gate before PR
pnpm typecheck— zero errors.pnpm lint— zero warnings.pnpm test— all green.pnpm --filter @hatch/web build— Next build succeeds.forge testif you touched contracts.
Pre-commit hooks (husky) run gitleaks + lint-staged automatically. Never
use --no-verify — if hooks fail, fix the root cause.
Database — Drizzle + migrations
Schema lives in apps/api/src/db/schema.ts.
Migrations are generated, reviewed, and committed.
# After editing schema.ts
pnpm --filter @hatch/api db:generate # → apps/api/drizzle/*.sql
pnpm --filter @hatch/api db:migrate # apply to DATABASE_URL
# Inspect state
pnpm --filter @hatch/api db:studio
Migration rules
- Always generate; never hand-write SQL into the migrations directory.
- Review the diff before committing. Drizzle sometimes generates unexpected drops on column order changes.
- Commit the migration in the same PR as the schema change.
- Run migrations locally before pushing. Staging is not your test env.
Contracts — Foundry
Four contracts in packages/contracts/src/:
HatchRegistry · HatchAttest · Hatcher · HatchNest.
cd packages/contracts
forge install foundry-rs/forge-std --no-commit # first time only
forge install OpenZeppelin/openzeppelin-contracts --no-commit
forge build
forge test -vvv
forge coverage --report summary # must be ≥ 95%
slither src/ # static analysis
Deploy script: DeployAll.s.sol. Documented in
ADR 0006.
Common workflows
Add a new public API route
- Add route handler in
apps/api/src/modules/public-api/routes.ts. - Add persist function in
persist.ts. - Extend OpenAPI spec in
openapi.ts. - Wire the dep through
ScoringRouterDeps/PublicApiDepsif factory-injected. - Add route test in
routes.test.ts(useappWith(deps)helper). - Mount in
apps/api/src/app.ts. - Add a typed client helper in
apps/web/src/lib/api.ts. - Document in
API.md.
Add a new scoring signal
See ADR 0004 before editing weights. Changing signal count or weights bumps the prompt version and breaks historical comparability. Prefer adding a sub-signal under an existing bucket.
Add a new chain adapter
- Create a new package under
packages/chain-<name>/. - Implement
ChainAdapter<T>from@hatch/chain-core. - Add types + stub that throws
not_implemented. - Wire into the api only after a signed pilot + audit coverage.
Solana + Base scaffolds in packages/chain-{solana,base}/ are templates.
Add a webhook event
See Webhooks guide and
ADR 0008. Emit via the shared
emit(event, id, payload) wrapper in apps/api/src/app.ts. Events are
fire-and-forget — producers never await.
Troubleshooting
Invalid environment configuration during tests
DATABASE_URL or ANTHROPIC_API_KEY isn't set. Add them to .env or
export them inline: DATABASE_URL=… pnpm test.
ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL on typecheck
Usually a composite project reference error. Run
pnpm --filter @hatch/api exec tsc --noEmit on the specific package to
see the real error.
Playwright tests show up in pnpm test
Known bug — Playwright specs are accidentally picked up by Vitest's glob.
Run pnpm --filter @hatch/web test vs test:e2e to isolate.
Admin panel 404s
You need ADMIN_TOKEN set on both the web (Next env) and the api
(loaded through env.ts). Web reads its own ADMIN_TOKEN; api validates
the bearer against env.ADMIN_TOKEN. Values must match exactly.
Attestation publish returns 503
BSC_RPC_URL, HATCH_ATTEST_ADDRESS, and ATTESTER_PRIVATE_KEY are
missing. This is fail-closed by design (see ADR 0006). Never run the
publisher against a production RPC without a signed contract at that
address.
Husky pre-commit fails with "commitlint failed"
Your commit message isn't conventional. Use type(scope): subject where
type is one of feat|fix|chore|docs|refactor|test|perf|build|ci.
Example: feat(scoring): add percentile window parameter.
Contributing
- Branch off
main:git checkout -b sprint/g-1-feed. - Follow the sprint spec in
docs/sprints/. - Test gate must be green before PR.
- Write the commit body to explain why, not what.
- PR template auto-populates; fill all sections.
- Tag at the sprint boundary (
v0.8.0-feedfor G.1, etc.).
See CONTRIBUTING.md for the detailed workflow and
SECURITY.md for disclosure policy.
Questions: open an issue tagged question, or drop into Telegram.