@usenaive-sdk/iac
v0.5.1
Published
Naïve Infrastructure-as-Code — declarative build-time config for governed agent profiles (defineConfig, cloud, runtime, agentTemplate, policy, defineModule).
Readme
@usenaive-sdk/iac
Purpose
Naïve Infrastructure-as-Code — the declarative, build-time, side-effect-free DSL you
author a naive.config.ts with. Nothing here makes a call; it produces typed config
objects that naive up and @usenaive-sdk/server consume.
The package carries two surfaces side by side.
The legacy surface — defineConfig, cloud.*, business.*, identity.*,
skills, policy(), agent(), agentTemplate(), runtime.pool(), defineModule(),
system(), normalizeConfig(), agentToTemplate(). Frozen and complete. Every export
keeps its name, its type and its output; a config written against it compiles and
normalizes byte-for-byte as before. Several constructs now carry @deprecated pointing
at their replacement — a pointer, not a removal notice.
The agent-runtime surface — additive, and validated only for configs that declare
company or teams:
| Declaration | What it is |
|---|---|
| brain({ name, scope, retention, writes, recall, visibility, partitions, lanes }) | A brain. See below. |
| brains({ default, declared }) | N brains and the one default, keyed by name. |
| team({ runtime, lead, agents, edges, brain }) | N agents behind one lead, on one runtime, bound to one partition. |
| runtime.durable({ residency, sleepAfter, workspace }) | The current runtime: durable, resolved by (team, tenant), optional isolated workspace. |
| runtime.hermes({ pool, isolation, autoscale }) | The legacy orchestration runtime, as a team-scoped reference. Frozen, @deprecated, never removed. |
| governance({ capabilities, spend, limits, residency, time }) | Closed-by-default capabilities; three spend classes; typed approvers. |
| secret.vault/env/connection/output | Credential references. The material is unrepresentable. |
| authoritySurface(config) | The privilege-bearing, prose-free, key-sorted projection a digest is taken over. |
Brains are first-class declarables — N per company, exactly one default
A company declares one brain with brain({...}) or several with brains({...}). Both
are the same concept at different arities: a team binds a partition, an agent binds a
view, and a binding is obtainable only as a method on the value that declares the
brain — so no expression can name a brain it was not handed.
const acme = brain({
retention: { beliefs: "365d", lessons: { fact: "180d" }, episodes: "30d" },
bounds: brainDefaults.bounds, // visible, overridable, never invented
writes: { mode: "review", promoteBy: "operator", scan: "enforce" },
recall: { keywords: true, vector: { floor: 0.3 }, graph: { hops: 2, limit: 8 } },
partitions: { support: { retention: { beliefs: "90d" } }, growth: {} },
lanes: { enabled: true, retention: { beliefs: "30d" } },
});
team({ runtime: runtime.durable(), lead: supportLead, brain: acme.partition("support") });
agent({ brain: acme.view({ partition: "support", can: ["recall", "attach"] }) });Several, when one company's work does not all belong in one knowledge base:
const hive = brains({
default: "core", // ONE field naming ONE key
declared: {
core: brain({ scope: "org", retention: { beliefs: "365d" }, writes: { mode: "review" },
partitions: { trunk: {} } }),
support: brain({ retention: { beliefs: "90d" }, writes: { mode: "propose" },
partitions: { tickets: {} } }),
legal: brain({ retention: { beliefs: "365d" }, writes: { mode: "review" },
visibility: { can: ["recall", "attach"] }, // nobody writes into legal
partitions: { contracts: {} } }),
},
});
company: { brains: hive }
team({ runtime: runtime.durable(), lead: counsel, brain: hive.partition("legal", "contracts") });
agent({ brain: hive.view("support", { partition: "tickets", can: ["recall", "propose"] }) });This mirrors the platform rather than constraining it: brain_knowledge_bases is keyed
(company_id, name) with a partial unique index on is_default = true, and
naive brain list / naive brain create --name --default have always driven it.
default is declared here and applied by no apply — say so before relying on it. The
field names which brain an unnamed binding in this config resolves to. It does not set
brain_knowledge_bases.is_default. The write that does is PATCH /v1/brain/{id}, reached
as naive brain default <name|id> or naive.brains.setDefault(id), and nothing in
naive up calls it. So a config saying default: "core" over a company whose live default
is "support" is a difference this DSL does not reconcile and one command does —
naive brain list reports which brain actually carries the flag, and that is the
authority. On a one-brain company the two cannot disagree.
What is unwritable rather than merely refused. A second default — default is one
field naming one key, exactly as team({ lead }) makes a second lead unwritable. A
binding to a brain nobody declared — hive.view(name, …) types name as keyof the
declared record, so a typo is a compile error, and hive.partition("legal", "tickets")
does not compile either because the partition type is the partitions of the named
brain. A second trunk — level is still the literal "company". The human verbs —
promote and forget are still not BrainAbilitys.
Scope is scope ("project" | "org", at most one org trunk per company), partitions
and lanes; retention is per content noun, per brain, narrowable per level and bounded
by a 365d ceiling; visibility is visibility.can — the ability ceiling for every view
of a brain — narrowed further by each per-agent ability list. An agent that must have no
access writes can: [], an empty ability list: not a second concept and not a second
brain.
A single-brain config is unaffected. company.brain still takes one Brain, an
unnamed brain still stamps no name onto its bindings, and authoritySurface() hashes a
pre-change config to the byte-identical digest — so adding this does not freeze existing
tenants behind a re-approval. packages/iac/src/__tests__/brains.test.ts pins that hash.
Consumers
@usenaive-sdk/server (type-only re-exports), @usenaive/api (normalizeConfig,
agentToTemplate), @usenaive-sdk/cli (naive init / naive up), and every customer
naive.config.ts. Zero runtime dependencies, by design — it must be safe to import from
a config file. Part of the Naive monorepo; see the root README for how
components fit together and which packages depend on this one.
Configuration
None. This package reads no environment and holds no state. It is configured via the
monorepo's shared env and package.json only in the sense that its consumers are; see
the root README's driver/env selector matrix for the services that act on its output.
Run standalone
pnpm --filter @usenaive-sdk/iac build
pnpm --filter @usenaive-sdk/iac typecheck # ALSO the type-refusal suite — see below
pnpm --filter @usenaive-sdk/iac testtypecheck is a test, not just a build step. src/__tests__/type-refusals.test.ts is a
list of @ts-expect-error assertions — a config that opens by default, names a
misspelled primitive, gives a team two leads, routes an edge to a member that does not
exist, or puts a literal where a SecretRef belongs. If any of those ever starts
compiling, typecheck goes red.
Place in the topology
The authoring root. One node in the Naive monorepo (open plumbing) with no inbound dependencies of its own; the root README has the full architecture diagram. Enforcement of what is declared here happens in the closed engine — this package only produces the declaration.
