---
title: Repository layout
description: 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.

```text
veefore/
├── 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.

```text
types ◀── 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
```

:::security 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](/secure/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 |

:::note `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](/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:

- `pg` is CommonJS, so under Node's ESM loader `await import('pg')` exposes only `default`. A named `{ Pool }` import was `undefined`.
- node-postgres returns an **array** of results for multi-statement SQL. Reading `.rows` off the array gave `undefined` and 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 |

:::note 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.

[→ Testing strategy](/architecture/testing-strategy)

## 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.

## Related

:::cards

:::card Architecture | /architecture
The shape and the principles.
:::

:::card Data model | /architecture/data-model
Tables and the amount rule.
:::

:::card Testing strategy | /architecture/testing-strategy
What each suite proves.
:::

:::card Local development | /build/local-development
Running it.
:::

:::
