DocsOpen sourceHow the code is laid out

How the code is laid out

Fifteen packages, and the whole point of the layout is that the two which decide anything depend on nothing.

packages/
  core/           domain types, constants, store + advisor ports, zero dependencies
  policy-engine/  deterministic evaluation + risk classification, zero dependencies
  local-gate/     in-process argument matching, so payloads never leave the machine
  memory/         team decisions as machine-checkable constraints
  risk/           behavioral, trust, verification, taint and dependency advisors
  content-shield/ offline secret + PII scanning, and the security baseline
  code-graph/     file-level import graph and blast radius
  runtime/        the gateway, HTTP server with RBAC, local stores, reporting
  postgres/       Postgres adapters for the storage ports
  redis/          Redis adapters for locks and session taint
  sdk/            TypeScript client
  mcp-firewall/   transparent MCP proxy
  intelligence/   optional BYOK layer: drafts and explains, never decides
  cli/            the memnox command, editor hooks, and the MCP server
  trust-bench/    the public governance benchmark
sdks/
  python/, go/    thin dependency-free clients
examples/
  policies/       ready-to-use policy files

The dependency direction is strict

core depends on nothing. policy-engine depends only on core. Everything else composes those.

Never import from another package's internals, only from its index.ts.

That is what keeps the two zero-dependency packages honest. The moment policy-engine reaches into runtime, the claim that policy evaluation is pure stops being checkable by reading one directory.

Where each decision stage lives

ActionGateway.authorize() in packages/runtime/src/action-gateway.ts runs the same five steps for every request. The gateway owns the pipeline; identity and approvals are collaborators it composes.

ActionGateway ──▶ AgentRegistry     identity, credentials, rotation
              └─▶ ApprovalService   raise, consent, quorum, break-glass
                        └─▶ evaluateConsent()   pure, in core
Every action walks the same five stages, on your own machine. The verdict is settled by the first stage with something to say about it, a blocked call never reaches the ones after it.

Stage

Identity

AgentRegistry, bearer hash first, then service-account JWT, then mTLS

Policy

PolicyEngine.evaluate() in @memnox/policy-engine

Advisors

@memnox/memory, @memnox/risk, @memnox/content-shield, @memnox/code-graph

Approval

ApprovalService.requestFor(), consentFor(), claimGrantFor()

Audit

JsonlAuditLog, and the Postgres adapter behind the same port

Two routes short-circuit that pipeline deliberately:

  • POST /v1/evaluate-risk runs steps 1, 3 and stops. It reports what the verdict would be without auditing anything or creating an approval, asking is not attempting.
  • POST /v1/context runs the same steps and renders them as a briefing through ActionGateway.briefbuildActionBriefing (in core) plus securityRequirementsFor (in content-shield). Both halves are lookups.

Extending the gateway

New escalation logic is an ActionAdvisor. @memnox/memory and @memnox/risk are the worked examples.

Three rules an advisor must satisfy, and a reviewer will check all three:

  1. It may only tighten a decision, never loosen one.
  2. It must be deterministic, same input, same escalation.
  3. Its failure must mean "no escalation", never a crash, never a blanket block.

The one exception to rule 3 is the taint advisor, which is fail-closed: an unreadable taint store means the session is treated as tainted. If you think your advisor needs the same exception, that is a conversation before it is a patch.

Ports and adapters

Storage sits behind small interfaces in core: IdentityStore, AuditLog, ApprovalStore, LockService. The local adapters are plain JSON and JSONL files; @memnox/postgres and @memnox/redis implement the same ports.

That is why "no account required" is true rather than marketing, the zero-infrastructure path is the same code path, not a demo mode. A new backend means implementing a port, not touching the gateway.

The pieces that make the guarantees checkable

Guarantee

Verdicts are reproducible

versionPolicySet content-hashes the rule set; policyVersion is stamped on every event

A rule change can be tested first

comparePolicySets, behind memnox policy simulate

Payloads never leave the machine

@memnox/local-gate matches arguments in-process

A model cannot decide

intelligence has no path into the gateway; IntentClassifier proposes, classifyRisk rates

Model-derived graph edges cannot decide

Only EXTRACTED AST edges cross into the decision path

A briefing is reproducible

SECURITY_BASELINE_VERSION and SHIELD_RULESET_VERSION

Reading order for a newcomer

  1. 1

    packages/core

    The vocabulary. Types, constants, ports, and evaluateConsent. Nothing here does IO, so it reads quickly.

  2. 2

    packages/policy-engine

    Matching, precedence and risk classification. Zero dependencies, so it is the easiest place to prove to yourself that evaluation is pure.

  3. 3

    packages/runtime/src/action-gateway.ts

    The five stages, in one file, in order.

  4. 4

    One advisor

    @memnox/memory is the smallest. It shows the whole advisor contract in a few hundred lines.