Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ technical compatibility.

## Current status

This checkout contains the merged M0-M5 implementation through the M5-03
reliability matrix. The project is maintained in the public
This checkout contains the merged M0-M5 implementation through the M5-06
performance gate. The project is maintained in the public
[OriginShift/AnteSig repository](https://github.com/OriginShift/AnteSig), and
the current public demo is [antesig.vercel.app](https://antesig.vercel.app).
The engineering foundation provides a pnpm workspace with a registered
Expand Down Expand Up @@ -142,6 +142,8 @@ or strengthen evidence semantics.
- [Known issues and operational limits](docs/known-issues.md)
- [Five-minute demo draft](docs/demo-script.md)
- [Reliability QA report](docs/reliability-report.md)
- [Performance acceptance report](docs/performance-report.md)
- [Production release runbook](docs/release-runbook.md)
- [M1 completion evidence and criteria](docs/m1-completion-evidence.md)
- [Governance](docs/governance.md)
- [Architecture decision records](docs/adr/README.md)
Expand Down
8 changes: 7 additions & 1 deletion apps/web/app/api/health/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { MOSS_BUILD_INFO } from "../../../src/server/moss-build-info";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";

const nodeProcess = (
globalThis as typeof globalThis & {
process: { readonly versions: { readonly node: string } };
}
).process;

export async function GET(): Promise<Response> {
const health = HealthResponseSchema.parse({
contractVersion: "0.1",
Expand All @@ -13,7 +19,7 @@ export async function GET(): Promise<Response> {
name: "antesig",
version: "0.0.0",
runtime: "nodejs",
nodeVersion: "22.23.1",
nodeVersion: nodeProcess.versions.node,
},
moss: MOSS_BUILD_INFO,
network: {
Expand Down
1 change: 1 addition & 0 deletions apps/web/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
import "./.next/types/root-params.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
5 changes: 5 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"version": "0.0.0",
"private": true,
"type": "module",
"packageManager": "pnpm@11.16.0",
"engines": {
"node": "22.x",
"pnpm": ">=11 <12"
},
"scripts": {
"dev": "next dev",
"start": "next start",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/contracts/health.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const HealthResponseSchema = z.strictObject({
name: z.literal("antesig"),
version: z.literal("0.0.0"),
runtime: z.literal("nodejs"),
nodeVersion: z.literal("22.23.1"),
nodeVersion: z.string().regex(/^22\.\d+\.\d+$/),
}),
moss: z.strictObject({
sourceMode: z.literal("INTEGRATION_FORK"),
Expand Down
8 changes: 7 additions & 1 deletion apps/web/test/contracts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ describe("health contract", () => {
name: "antesig",
version: "0.0.0",
runtime: "nodejs",
nodeVersion: "22.23.1",
nodeVersion: process.versions.node,
},
moss: {
sourceMode: "INTEGRATION_FORK",
Expand Down Expand Up @@ -297,5 +297,11 @@ describe("health contract", () => {
expect(
HealthResponseSchema.safeParse({ ...health, timestamp: "now" }).success,
).toBe(false);
expect(
HealthResponseSchema.safeParse({
...health,
app: { ...health.app, nodeVersion: "24.0.0" },
}).success,
).toBe(false);
});
});
2 changes: 1 addition & 1 deletion apps/web/test/production-start.smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const expectedHealth = {
name: "antesig",
version: "0.0.0",
runtime: "nodejs",
nodeVersion: "22.23.1",
nodeVersion: process.versions.node,
},
moss: {
sourceMode: "INTEGRATION_FORK",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/test/routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ describe("GET /api/health", () => {
name: "antesig",
version: "0.0.0",
runtime: "nodejs",
nodeVersion: "22.23.1",
nodeVersion: process.versions.node,
},
moss: {
sourceMode: "INTEGRATION_FORK",
Expand Down
5 changes: 5 additions & 0 deletions config/production.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Public production defaults to the deterministic Fixture-capable profile.
CLEAR402_ENABLED=false

# MOSS_RPC_URL is intentionally absent. The hosted Web route has no Live
# session, and a missing value must remain an explicit LIVE_UNAVAILABLE result.
109 changes: 109 additions & 0 deletions docs/release-runbook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Production release runbook

## Release contract

AnteSig is deployed as a Vercel Next.js project at
<https://antesig.vercel.app>. The project Root Directory is `apps/web`, whose
`package.json` pins Vercel builds and Functions to Node `22.x`; Vercel may
update the minor and patch release. `/api/health` reports the actual
`process.versions.node` value and rejects a non-22 runtime.

The public URL requires no login. The production profile is intentionally
bounded:

- `CLEAR402_ENABLED=false` disables Credential actions and browser resources.
- `MOSS_RPC_URL` is not configured or consumed by the hosted Web route. A Live
request must fail with `503 LIVE_UNAVAILABLE` and must not return a report or
Decision.
- Fixture requests remain deterministic and return `provenance: FIXTURE`.
- `/api/health` exposes only bounded configuration and Moss build identity. It
must never expose a secret, credential, private key, hostname, filesystem
path, or full RPC URL.

Start from `config/production.env.example`. Do not commit `.env` files,
credentials, Vercel tokens, private keys, or RPC URLs. A missing optional
variable is the supported public baseline; it must not prevent startup.

## Pre-deployment gate

Run from a clean checkout of the exact release commit with Node `22.23.1` and
pnpm `11.16.0`:

```bash
corepack enable
pnpm install --frozen-lockfile
ASDF_NODEJS_VERSION=22.23.1 pnpm audit --prod --audit-level=moderate
ASDF_NODEJS_VERSION=22.23.1 pnpm check
ASDF_NODEJS_VERSION=22.23.1 CLEAR402_ENABLED=false pnpm build
ASDF_NODEJS_VERSION=22.23.1 pnpm test:web:production
ASDF_NODEJS_VERSION=22.23.1 pnpm test:web:production
```

The two production-start runs each allocate a new port, start a fresh Next.js
process, wait for health, exercise the page and logo, and stop the process.
They are the repeatable restart/cold-process smoke. They do not claim that a
specific Vercel request reached a newly allocated serverless isolate.

For a manual local fallback:

```bash
ASDF_NODEJS_VERSION=22.23.1 CLEAR402_ENABLED=false pnpm build
ASDF_NODEJS_VERSION=22.23.1 CLEAR402_ENABLED=false PORT=3000 pnpm start
curl -fsS http://127.0.0.1:3000/api/health | jq
BASE_URL=http://127.0.0.1:3000 ASDF_NODEJS_VERSION=22.23.1 pnpm test:e2e:smoke
```

## Deploy and verify

Production deploys must come from the merged `main` commit after its GitHub
`quality-gate` succeeds. Link the checkout to the existing project, inspect
the target, deploy, then record the immutable deployment URL before changing
traffic:

```bash
npx --yes vercel@latest link --yes --project antesig --scope <team-slug>
npx --yes vercel@latest project inspect antesig --scope <team-slug>
npx --yes vercel@latest deploy --prod --yes --scope <team-slug>
```

Verify the public alias and the immutable deployment URL:

```bash
curl -fsS https://antesig.vercel.app/api/health | jq
BASE_URL=https://antesig.vercel.app ASDF_NODEJS_VERSION=22.23.1 pnpm test:e2e:smoke
```

Required observations are HTTP 200 health, `app.nodeVersion` beginning `22.`,
`network.configured=false`, the expected Clear402 flag, a working Fixture
flow, explicit fail-closed Live behavior, and no authentication challenge.
Check HTTPS/HSTS headers as a separate observation. Never infer Live chain
evidence from health or Fixture success.

## Rollback

Before deployment, use `vercel ls antesig` to identify the current immutable
production URL. Select only a previously verified healthy production
deployment as the rollback target; do not guess from timestamps or aliases.
During an incident, the release owner runs:

```bash
npx --yes vercel@latest rollback <verified-good-deployment-url> --scope <team-slug>
npx --yes vercel@latest rollback status --scope <team-slug>
curl -fsS https://antesig.vercel.app/api/health | jq
BASE_URL=https://antesig.vercel.app ASDF_NODEJS_VERSION=22.23.1 pnpm test:e2e:smoke
```

Rollback repoints production traffic without rebuilding. Vercel disables
automatic production assignment after a rollback; after the incident is
resolved, restore a newly verified deployment explicitly:

```bash
npx --yes vercel@latest promote <verified-fixed-deployment-url> --scope <team-slug>
```

If the hosted service cannot be restored, use the local production fallback
above and describe it as local production, never as the public deployment.
The command behavior and plan limits are defined by Vercel's
[rollback CLI](https://vercel.com/docs/cli/rollback) and
[production rollback](https://vercel.com/docs/deployments/rollback-production-deployment)
documentation.