@usenaive/region-core. Services never import
a provider SDK directly; they call a driver resolved from a NAIVE_*_DRIVER env
var. The interfaces are 1:1 with the cloud implementations they abstract, so the
cloud code paths port to a Tower without behavior change.
The five selectors
Defaults come from
resolveDriverSelection(): with NAIVE_BUILD=tower the base
is the Tower set, otherwise the cloud set. Any individual NAIVE_*_DRIVER value
overrides its base. (There are also kv and cron drivers — both Postgres on a
Tower — used for the relay cursor and scheduled sweeps.)
The runtime pool is a selectable driver, not a fixed dependency. The cloud
default is the ECS warm pool; a Tower runs
local (docker-compose service
instances). Neither is privileged by the code.NAIVE_SECRETS_DRIVER — the Credential Vault
The SecretsDriver is envelope encryption. Regardless of driver the stored
value layout is identical — nonce(12) || tag(16) || ciphertext, AES-256-GCM,
with an encryption context of exactly { company_id, tenant_user_id } — so
vault_entries rows are portable between substrates.
generateDataKey / decryptDataKey / reEncryptDataKey are the required
methods; decryptDataKey must reject on a mismatched context. reveal is
optional.
NAIVE_QUEUE_DRIVER — durable queues
sqs (cloud) and pg (Tower) implement the same QueueDriver: create/delete,
send (with FIFO group id + content dedup + delay), receive with a visibility
timeout, delete, purge, and attributes.
The pg driver implements pgmq semantics on plain SQL (SKIP LOCKED,
read_ct, archive) rather than requiring the pgmq extension — a documented
deviation, since FIFO group-key withholding needs custom SQL either way. It runs
on stock postgres:16, does FIFO group-withholding + content dedup, and moves a
message to the DLQ at maxReceive = 5.
NAIVE_INFERENCE_DRIVER — chat completions
An OpenAI-compatible InferenceDriver (non-streaming, streaming, and
listModels). Both the llm primitive and the agent proxy (/v1/proxy/*) go
through the same router (packages/api/src/services/llm.ts → drivers.ts),
so a box has one inference source, not two.
The three box inference modes
On a Tower the mode (persisted per box inboxes.inference_mode, chosen in the
dashboard Settings → Inference or the onboarding wizard) decides where model
calls actually go. The effective mode resolves as env override
(NAIVE_INFERENCE_MODE / NAIVE_LLM_FORWARD / NAIVE_INFERENCE_DRIVER) → the
persisted choice → cloud.
If a box is set to
cloud but no forward is wired, it degrades to the local
provider driver (BYO key) rather than failing — so cloud is safe as a default.
NAIVE_STORAGE_DRIVER — object storage
StorageDriver = put / get / presigned URL / delete.
s3(cloud) — AWS S3, real presigned URLs.fs(Tower) — files underNAIVE_STORAGE_FS_ROOT(e.g./data/storage). Fetch URLs are HMAC-signed and served by/storage/local/*key; it guards against path traversal.
NAIVE_RUNTIME_DRIVER — the runtime pool
RuntimePoolDriver is the spec’s “supervisor”: claim/release/get/list of runtime
instances for tenants.
ecs(cloud) — the ECS warm pool (hot-claim an idle task).local(Tower) — the box’s composeagent-runtimeservice (the real Hermes agent-container). It boots inPOOL_MODE=idle; a claim HOT-CLAIMS it in place by POSTing/provision(identical contract to the ECS path — no restart), then records the binding sosidecarFetch/getContainerForSubjectresolve it. Point the driver at it withNAIVE_LOCAL_RUNTIME_URL(defaulthttp://agent-runtime:3100) +CONTAINER_AUTH_TOKEN.
NAIVE_RUNTIME_TOKEN and
addresses the egress gateway as its only route
off-box. The agent-container image is built for arm64 and amd64
(scripts/build-agent-container.sh).
Local inference. Set
HERMES_LOCAL_MODEL (+ NAIVE_VLLM_URL) to point the
box’s Hermes runtime at a local vLLM OpenAI-compatible endpoint — the CEO’s own
reasoning then stays on-box, complementing the auto inference driver that
keeps the llm primitive local too.The GovernorClient contract
Drivers move bytes; they never make the trust decision. That decision is a wire call to the closed governor, and the only way to make it isGovernorClient / the governorCall() wrapper in @usenaive/region-core
(pinned in PROTOCOLS.md §1).
- Transport — localhost HTTP/1.1 + JSON to
GOVERNOR_URL(defaulthttp://governor:8700), authenticated with a sharedGOVERNOR_TOKENbearer, ajv-validated on both ends. - Fail-closed is baked in.
governorCall()bounds every call (500 ms connect / 2 s total). Governor unreachable or timeout throwsGovernorUnavailableError; there is no verdict caching and no call site can default to library behavior. The API maps it to403 enforcement_unavailable; the gateway maps it to deny-all. - A
Verdictcarriesdecision, atargetoflive | sandbox(whether an allowed action routes to real rails or the sandbox adapter), and — onfreeze— anapproval_idfor a persisted pending row.
Writing a driver
- Implement the interface from
@usenaive/region-core(e.g.SecretsDriver,QueueDriver). Match the exact method shapes; for secrets, preserve thenonce(12) || tag(16) || ciphertextlayout and reject on context mismatch sovault_entriesrows stay portable. - Register it behind the selector. The api resolves drivers in
packages/api/src/drivers.tsvia lazy getters keyed onresolveDriverSelection(process.env); add your branch there (and export acreate…Driver(opts)factory from your package, as the bundled drivers do). - Never talk to the governor yourself except through
GovernorClient— enforcement stays in the closed engine, and the fail-closed guarantee depends on the single wrapper. - Add a conformance capability. Tag any leg that needs a real third-party
key with
hasCapability()so a missing key is reportedBLOCKED, never mocked (see Conformance). - Ship a README with the five-section template (Purpose / Consumers / Configuration / Run standalone / Place in the topology) — the docs gate checks it.
The set of selectors is the source of truth in
packages/core/src/drivers.ts
(DRIVER_ENV_VARS). A CI gate greps this page for every one:
NAIVE_SECRETS_DRIVER, NAIVE_QUEUE_DRIVER, NAIVE_INFERENCE_DRIVER,
NAIVE_STORAGE_DRIVER, NAIVE_RUNTIME_DRIVER.