2026-03-10 00:00:00 -07:00
# APOPHIS
Behavioral confidence for Fastify services.
APOPHIS checks whether route behavior holds across operations, states, and protocol flows.
2026-05-21 20:39:36 -07:00
Inspired by the concept of invariant-driven automated testing: instead of only checking payload shape, APOPHIS encodes intended behavior as executable contracts and verifies them with property-based and stateful testing.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
Supported Node.js versions: >=20.18.1 (20.x) and 22.x.
2026-03-10 00:00:00 -07:00
``` bash
2026-05-21 20:39:36 -07:00
npm install @apophis/fastify fastify @fastify/swagger
npx apophis init --preset safe-ci
npx apophis verify --profile quick --routes "POST /users"
2026-03-10 00:00:00 -07:00
```
2026-03-10 00:00:00 -07:00
`x-ensures` is an OpenAPI schema extension for behavioral contracts — statements about what a route must guarantee.
2026-03-10 00:00:00 -07:00
## Cross-Route Failure Example
Add one behavioral contract next to a route schema. APOPHIS can verify cross-route behavior, such as whether a resource created by one route is retrievable through another.
**Route: **
``` javascript
2026-03-10 00:00:00 -07:00
import crypto from 'crypto' ;
2026-03-10 00:00:00 -07:00
app . post ( '/users' , {
schema : {
'x-category' : 'constructor' ,
'x-ensures' : [
// BEHAVIORAL: Creating a user must make it retrievable
'response_code(GET /users/{response_body(this).id}) == 200'
]
}
} , async ( request , reply ) => {
const { name } = request . body ;
2026-03-10 00:00:00 -07:00
const id = ` usr- ${ crypto . createHash ( 'sha256' ) . update ( name ) . digest ( 'hex' ) . slice ( 0 , 8 ) } ` ;
2026-03-10 00:00:00 -07:00
reply . status ( 201 ) ;
return { id , name } ;
} ) ;
```
**APOPHIS output: **
``` text
Contract violation
POST /users
Profile: quick
Seed: 42
Expected
response_code(GET /users/{response_body(this).id}) == 200
Observed
2026-03-10 00:00:00 -07:00
GET /users/usr-7d865e returned 404
2026-03-10 00:00:00 -07:00
Why this matters
The resource created by POST /users is not retrievable.
Replay
apophis replay --artifact reports/apophis/failure-2026-04-28T12-30-22Z.json
Next
Check the create/read consistency for POST /users and GET /users/{id}.
```
JSON Schema cannot express this relationship. APOPHIS turns it into an executable check.
## Three Modes
| Mode | Purpose | Default Environments |
|---|---|---|
| `verify` | Deterministic CI and local contract verification | local, test, CI |
| `observe` | Runtime visibility and drift detection without blocking | staging, prod |
| `qualify` | Exercise scenarios, stateful flows, and configured chaos checks before release | local, test, staging |
## Quickstart: 3 Commands
``` bash
# 1. Install
2026-05-21 20:39:36 -07:00
npm install @apophis/fastify fastify @fastify/swagger
2026-03-10 00:00:00 -07:00
# 2. Scaffold
2026-05-21 20:39:36 -07:00
npx apophis init --preset safe-ci
2026-03-10 00:00:00 -07:00
# 3. Verify
2026-05-21 20:39:36 -07:00
npx apophis verify --profile quick --routes "POST /users"
2026-03-10 00:00:00 -07:00
# 4. Doctor
2026-05-21 20:39:36 -07:00
npx apophis doctor
2026-03-10 00:00:00 -07:00
```
See [docs/getting-started.md ](docs/getting-started.md ) for the full walkthrough.
## Trust and Safety
- **Deterministic replay**: Every failure includes a seed and a one-command replay.
2026-03-10 00:00:00 -07:00
- **Explicit test budget**: Control how many tests run with `runs: 10` in your preset.
2026-03-10 00:00:00 -07:00
- **CI-safe default path**: `verify` is deterministic and safe for CI pipelines.
2026-03-10 00:00:00 -07:00
- **Machine-readable output**: `--format json-summary` and `--format ndjson-summary` for CI dashboards.
2026-03-10 00:00:00 -07:00
- **Production-safe observe path**: `observe` is non-blocking by default. Blocking behavior requires explicit break-glass policy.
- **Qualify path gated away from prod**: `qualify` is blocked in production by default.
2026-03-10 00:00:00 -07:00
- **Monorepo workspace support**: `--workspace` fans out `verify` and `doctor` across all packages.
2026-03-10 00:00:00 -07:00
- **Explicit environment boundaries**: Config rejects unknown keys and unsafe environment mixes.
## LLM-Safe
APOPHIS gives coding agents a constrained, repeatable way to encode and verify behavior:
- Official scaffolds (`safe-ci` , `llm-safe` , `platform-observe` , `protocol-lab` )
- `apophis doctor` checks for missing dependencies, malformed config, and unsafe modes
- CI policy guards catch unknown keys, unsafe environments, and missing seeds
- Generated code follows the same pattern in every repo
See [docs/llm-safe-adoption.md ](docs/llm-safe-adoption.md ) for templates and CI policy.
## Full Documentation
- [Getting Started ](docs/getting-started.md ) — First route, first verify run, first replay
- [CLI Reference ](docs/cli.md ) — All 7 commands, global flags, exit codes
- [Verify Mode ](docs/verify.md ) — Deterministic contract verification
- [Observe Mode ](docs/observe.md ) — Runtime visibility and drift detection
- [Qualify Mode ](docs/qualify.md ) — Scenarios, stateful testing, chaos
2026-03-10 00:00:00 -07:00
- [Quality Engines ](docs/quality.md ) — Chaos injection, flake detection, mutation testing
2026-03-10 00:00:00 -07:00
- [Performance ](docs/performance.md ) — Repeatable benchmarks and CPU profiling
- [LLM-Safe Adoption ](docs/llm-safe-adoption.md ) — Scaffolds and CI guards
2026-03-10 00:00:00 -07:00
- [Protocol Extensions ](docs/attic/protocol-extensions-spec.md ) — JWT, X.509, SPIFFE, WIMSE
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
## Recommended Integration
**New projects: ** Use `createFastify()` to ensure route discovery is installed before any routes are registered.
``` ts
import { createFastify } from '@apophis/fastify'
const app = await createFastify ( {
logger : true ,
2026-05-22 11:05:52 -07:00
apophis : {
runtime : process.env.NODE_ENV === 'test' ? 'error' : 'off' ,
observe : process.env.NODE_ENV === 'production'
? { enabled : true , sampling : 0.1 , sinks : [ metricsSink ] } // your ObserveSink
: undefined ,
} ,
2026-05-21 20:39:36 -07:00
} )
// Register swagger, auth, plugins, and routes after app creation.
```
**Existing projects: ** Register APOPHIS or install route discovery before routes. Run `apophis doctor` to verify routes are discovered with full schema metadata.
**Schema-less fallback: ** If APOPHIS is registered after routes, `printRoutes()` can recover paths but not route schemas or behavioral contracts. `apophis doctor` and `apophis verify` will warn when discovery is schema-less.
## Current Limitations
These reflect current implementation behavior. All are actively tracked for improvement.
- **Route discovery requires ordering.** If the APOPHIS plugin or route discovery hook is not installed before routes are registered, behavioral contract annotations (x-ensures, x-requires, x-outbound, x-variants, x-timeout) cannot be recovered. Use `createFastify()` for new projects or register APOPHIS early.
2026-05-22 14:00:47 -07:00
- **Observe is programmatic.** Register `apophisPlugin` with `observe: { enabled: true, sinks: [...] }` for non-blocking contract evaluation on live traffic. Use `apophis doctor --mode observe` to validate config before deploying. See `docs/observe.md` .
2026-05-21 20:39:36 -07:00
- **CLI verify samples once per contract by default.** Set `runs` in your preset to increase the number of property-based test samples per route. The programmatic `fastify.apophis.contract()` API supports the same `runs` configuration.
2026-05-22 15:13:32 -07:00
- **Outbound mocks are scoped per async context.** Uses `AsyncLocalStorage` so concurrent tests get isolated mock runtimes. No process-global lock.
2026-05-21 20:39:36 -07:00
- **Qualify coverage depends on profile configuration.** Qualify runs scenario, stateful, and chaos checks based on profile gates. Chaos route selection uses the configured strategy (one/all/sample/routes).
## Compatibility
- **Fastify v5 only.** Fastify v4 and earlier are not supported.
- **ESM only.** This package is `"type": "module"` and does not provide a CommonJS build. Use `import` syntax.
- **Node.js `>=20.18.1 <21 || >=22 <23` **.
- **`@fastify/swagger` must be registered before routes** (APOPHIS auto-registers it if missing).
2026-03-10 00:00:00 -07:00
## License
2026-03-10 00:00:00 -07:00
MIT