Repository layout
Where everything lives, and the dependency direction that keeps it that way.
An npm workspace monorepo. One package.json at the root with the shared
dependencies, and a workspace per package.
textveefore/
├── packages/
│ ├── types/ shared vocabulary — no runtime dependencies
│ ├── common/ config, logging, redaction, crypto, resilience, telemetry
│ ├── db/ driver, migrations, repositories, schema catalogue
│ ├── identity/ key handling, JWS
│ ├── policy/ evaluator, schema, fields, templates, diff, Rego export
│ ├── sdk-js/ the TypeScript SDK
│ ├── sdk-python/ the Python SDK
│ └── cli/ the `veefore` command
├── services/
│ └── core/ every domain service
├── apps/
│ ├── control-plane/ the management HTTP surface + the worker
│ └── console/ the browser app
├── gateway/ the enforcement HTTP surface
├── docs/ this site
├── examples/ four working programs
├── scripts/ build, verify, migrate, seed, smoke tests, audits
├── tests/ seven suites
└── infrastructure/ docker, kubernetes, helm, terraform
#Dependency direction
Strictly one way. Nothing on the left depends on anything to its right.
texttypes ◀── common ◀── db ◀── policy ◀── services/core ◀── control-plane
◀── gateway
identity ◀──────────────────────────────────────────────┘
sdk-js, sdk-python, cli ── depend on types only (and the wire contract)
console ── depends on types only
Why the SDKs depend only on `types`
An SDK that imported services/core could, in principle, contain logic that
looked like authorization. It would also be enormous, and it would mean
publishing server internals to every consumer.
Depending only on the shared vocabulary makes it structurally impossible for the SDK to decide anything. It can only describe a request and interpret a response. → The boundary
The console likewise depends only on types. It cannot evaluate a policy even
by accident.
#packages/types
The vocabulary. No runtime dependencies at all.
enums.ts |
Lifecycle states, decisions, outcomes, stages, severities, signal types |
permissions.ts |
The permission list, roles, high-impact and step-up sets |
policy.ts |
The policy IR, fields, operators, evaluation input and output |
domain.ts |
Domain entities |
gateway.ts |
The gateway wire contract |
events.ts |
Event types, and which are webhook-subscribable |
errors.ts |
ERROR_CODES — the canonical table |
api.ts |
API request and response shapes |
Several of these are the source for a generated reference page, which is why they are worth keeping as plain data rather than as logic.
#packages/common
config.ts |
Environment validation. Invalid config stops the process |
logger.ts |
pino, with redaction applied on ingest |
redaction.ts |
The deny-lists that strip secrets before a line is constructed |
crypto.ts |
Sealing, HMAC, canonical JSON |
errors.ts |
VeeforeError, carrying a code and a safe message |
pagination.ts |
Cursor encoding |
rate-limit.ts |
In-process or Redis-backed |
resilience.ts |
Circuit breakers, timeouts |
telemetry.ts |
Every Prometheus metric |
request-context.ts |
Request and trace id propagation |
money.ts |
Minor-unit arithmetic. Formatting and comparison only |
kv.ts, ids.ts, time.ts |
Small utilities |
`money.ts` does arithmetic, not movement
Minor-unit handling for comparing an amount against a threshold without floating-point error. There is nothing in it that transfers anything. → Scope
#packages/db
driver.ts abstracts PostgreSQL and the embedded PGlite behind one interface.
Three bugs worth knowing about were fixed here, and each is documented in a
comment at the site:
pgis CommonJS, so under Node's ESM loaderawait import('pg')exposes onlydefault. A named{ Pool }import wasundefined.- node-postgres returns an array of results for multi-statement SQL. Reading
.rowsoff the array gaveundefinedand threw after the DDL committed — so the schema applied but was not recorded. close()on the embedded driver lazily booted a fresh empty database on the next query, which left the gateway's readiness check green with no data behind it.close()is now terminal.
repository.ts and repositories.ts implement the tenant scope guard. Every
method takes a scope and refuses without one.
#services/core
One file per domain, and enforcement.ts is the one to read first — it
implements the twelve-stage pipeline.
enforcement.ts |
The pipeline |
agents.ts, agent-identity.ts, passport.ts |
Identity and lifecycle |
policies.ts, simulation.ts |
Authoring, versioning, simulation |
delegation.ts |
Chain resolution and the escalation refusal |
risk.ts |
The thirteen signals |
approvals.ts |
Routing, quorum, expiry |
credentials.ts |
The broker, sealing and leasing |
tools.ts, tool-runtime.ts |
The registry and egress |
audit.ts, audit-query.ts |
The hash chain |
trust.ts |
Evidence |
incidents.ts |
Investigations |
a2a.ts |
Peer connections |
jobs.ts |
The queue |
webhooks.ts |
Signing and delivery |
tenancy.ts, team.ts, authorization.ts, auth.ts |
Tenancy and access control |
demo-seed.ts, seed.ts |
Seeding |
#scripts
The ones worth knowing:
verify-all.mjs |
The 23-stage gate. npm run verify |
scope-audit.ts |
Fails the build if an out-of-scope feature exists |
quality-audit.ts |
Fails it on mock data, hardcoded metrics, auth bypass, fail-open, committed secrets |
migrate.ts |
Apply, or --check against a throwaway database |
seed.ts |
Empty or --demo |
smoke.ts, smoke-gateway.ts, smoke-sdk.ts, smoke-python-sdk.ts, smoke-cli.ts, smoke-console.ts |
Real processes, real HTTP |
validate-infrastructure.mjs |
Container, Kubernetes, Helm, Terraform |
lib/isolated-env.ts |
Test isolation that verifies pinning took effect, and clears ambient VEEFORE_* variables |
Why `isolated-env.ts` verifies rather than assumes
A module-scope getLogger() once froze the whole app config at import time, so
the smoke tests' isolation was silently ignored and they wrote five
organizations into the shared database.
The helper now asserts that pinning actually took effect. Isolation you have not verified is isolation you do not have.
#tests
Seven suites, one Vitest project each: unit, integration, contract,
policy, security, e2e, chaos. Plus the Python SDK's own pytest suite and
the console's Vitest suite.
#Conventions
Explicit .ts extensions in imports. Node's ESM resolver requires them, and
the project does not use a bundler for the backend.
No barrel re-exports across package boundaries except each package's
index.ts. Keeps the dependency graph readable.
Comments explain why, not what. Several of the comments in driver.ts
and isolated-env.ts document a specific bug, because the next person to touch
that code needs the reason, not a restatement of the line.
One file per domain in services/core. Large files, deliberately. The
alternative is a service split across eight files where the pipeline is harder
to follow than the problem it solves.