BEH.DEV

A personal AI company · city hall

Build the city before the brains.

Beh.dev is one human principal, a permanent staff of machine scientists and engineers, and infrastructure owned and operated end to end. It began with no real model at all — on purpose. The first one now runs on the laptop's GPU; the city around it is still being built.

If the city works with fake brains, it will work with real ones. If it only works with real ones, we never built a city — we built a wrapper around someone else's API. Charter · the one-sentence test
Substrate k3s · three nodes · not up yet Foundry stub + local 27B · verified Citizens 8 roles · 4 specced
City Bus nats jetstream simulated
Subject
city.goals.>
Verdict
—
Cycle
000

Thesis 01

A research organisation is not a pile of models.

It is a set of roles, rules, records and resources. Build those four correctly and the models become a swappable component — which is the whole reason this city starts with a stub where its intelligence should be.

roles
A citizen is defined by a declarative spec, not a prompt buried in code. A role states its purpose, its permitted tools, its data clearance, its budget, and who reviews its output. Changing the workforce is editing YAML, not rewriting a program.
rules
Nothing is enforced by convention or good intentions. Admission policy rejects non-compliant workloads. NetworkPolicy makes exfiltration a routing impossibility rather than a promise. Budgets are enforced at the gateway, so a runaway agent stops rather than bills.
records
Every run is replayable or it did not happen. Inputs hashed, config and seed captured, every model output kept by its hash, the whole causal chain queryable — and replayable — months later. A scientist that cannot show its work is a random number generator with good manners.
resources
The substrate is a detail we deliberately refuse to hard-code. Today the city is one laptop: a real model on its GPU, and a stub for everything else. Tomorrow it runs on rented GPUs. That transition must be a configuration change, because any architecture that makes it a migration is the wrong architecture.

The city 02

The metaphor is load-bearing.

Every district is a real component with a real job, not a naming flourish. If a district cannot name the software that implements it, it does not exist — and a component without a district is one nobody has placed.

The last column says how far each one is. Designed: written down. Built: it exists and runs. Verified: a test that can fail is green. Most of the city is still designed.

DistrictFunctionWhat it actually isState
Land & utilitiesCompute, storage, powerk3s cluster, CSI storage classes, resource quotasdesigned
Roads & postHow citizens reach one anotherNATS JetStream — subjects, queues, KV, object storedesigned
CitizensThe scientists and engineersAgent pods: an image + a role spec + an identity + a budgetdesigned
WorkplacesWhere work happensSandboxed Jobs and Argo Workflows, default-deny networkingdesigned
FoundryThe source of thoughtmodel-gateway → dummy / local / frontier. The local model runs on the host: Docker on macOS cannot reach the GPUstub, model verified
gateway designed
Records officeWhat was done, and what is trueMLflow runs, MinIO artifacts, the gateway ledger, OTel tracesdesigned
LibraryAccumulated knowledgePostgres + pgvector: papers, results, prior art, agent memorydesigned
Border controlWhat may leave, carrying whatEgress policy at the gateway; NetworkPolicy at the poddesigned
Civil registryWho is whoServiceAccounts, projected tokens, a registry of outside callersdesigned
Planning officeWhat the city should look likeFlux, reconciling the cluster from Gitdesigned
Permit officeWhat may be built at allKyverno admission: unlabelled, privileged or unapproved workloads are refuseddesigned
StrongroomWhere the keys are keptSOPS + age in Git, Vault + External Secrets in the cluster, the Keychain on the hostdesigned
ObservatoryWhat is happening, and how fastPrometheus, Loki, Tempo + OpenTelemetry, Grafanadesigned
Front doorHow the human reaches the city from anywhereA tailnet address → the companion, which checks every caller and every skill callchecks verified
daemon designed
City hallDirection, budget, vetoThe human — approval queues, dashboards, kill switchesdesigned

Citizens 03

An agent is four things.

An image, a role spec, an identity, and a budget. Nothing else distinguishes one citizen from another — which is what makes the workforce editable rather than rewritable.

RoleJobReview gate
principalTurns goals into decomposed, prioritised ticketshuman
scientistDesigns experiments, forms hypotheses, interprets resultsreferee · 3 rounds
engineerWrites and fixes the code an experiment needsreferee · 3 rounds
refereeAdversarial review. Can reject and return work.principal
librarianIngests papers and data into pgvector; answers retrieval—
opsWatches cluster health; raises findings the principal ticketshuman
scribeTurns accepted runs into readable reportshuman
companionTalks with the human; the only citizen reachable from outside the machinehuman, per effect
The Referee is deliberately adversarial and cannot be the author of what it reviews. Self-approving agents produce confident nonsense at scale. Architecture · L4 citizens

That separation is not a guideline. It is a field in the spec, and the spec is what will run once the dispatcher that reads it is built. The scientist cannot publish a result at all: it can only ask for review, so its work reaches the principal and the human through a verdict or not at all.

agents/roles/referee.yaml — excerpt
  # Deliberately cooler than the Scientist: this role is not here to be creative.
  model: { primary: beh-reason-lg, temperature: 0.2 }
  clearance: internal
  tools:
    - name: vector.search
    - name: mlflow.read         # read-only: a referee cannot edit the record
    - name: artifact.get
  denied: [ artifact.put, mlflow.log, code.execute, ledger.write, net.egress.internet ]
  constraints:
    # …
    never_review_own_authorship: true
    must_cite_evidence: true    # a verdict without evidence is itself rejected
  # …
  messaging:
    subscribe: [ "city.review.requests.>" ]
    publish:   [ "city.review.verdicts.>" ]
  outputs:
    required: [ verdict, evidence, severity, required_changes ]
    verdict_enum: [ accept, revise, reject ]

Operating 04

Seven principles that decide arguments.

Each one exists to settle a class of decision in advance, so the same debate is not had twice.

i

Fake the intelligence, never fake the interface.

Stub backends return deterministic, seeded, cost-accounted responses through the exact API a real model serves — tool calls and structured output included. Contracts are exercised from day one.

ii

One door per trust domain.

Every model call and every web search crosses model-gateway. One chokepoint is auditable; twelve SDKs scattered through agent code are not. The laptop and the cluster are separate domains, so each gets one door, and restricted data never passes between them.

iii

Portability is a labelling discipline.

Workloads select an abstract tier, never a machine — one label key per tier, so a laptop node can wear several hats. In the cloud those hats are separate node pools. The base manifests are identical; each environment differs by one reviewed overlay.

iv

Determinism is a research instrument.

Same seed, same inputs, same outputs. That makes stub mode a load-testing and correctness tool rather than a placeholder. A real model is only as deterministic as its environment, so its outputs are kept and replayed rather than re-run.

v

Least privilege by construction.

An agent gets exactly the subjects, buckets, tables and egress it declares. The default is nothing.

vi

The human is a scheduled component.

Approval gates are part of the pipeline with defined SLAs and queues, not interruptions to it.

vii

Name the reason, or park it.

Before a component is added, it names the requirement it serves. If it serves none, it is parked — Kafka, OpenShift and Backstage are, for now.

Determinism principle iv
Run A / Run B
0000 / 0000
Seed A / B
20260921 / 20260921
Divergence
0.000

Two runs, one seed, one scheduler. The divergence readout is measured rather than asserted, and it can fail: perturb run B — give it the next seed — and it leaves zero at once; restore it and both runs restart on one seed and hold at zero. That is the property dummy-inference gives the foundry. Recorded on 10 October 2026: the same verdict request, sent twice, returned content hash 3cf31417ed54cfdf both times; with the seed one higher it returned af053a5574dccabc.

The real model held too, measured the same day: a greedy answer came back identical with and without a second request sharing its batch. That holds on this laptop and this build, which is why the records keep outputs rather than trusting a re-run.

It is already shipped once. Juju, the company’s second product, runs its speech model at build time rather than at runtime — every line is reviewed before a child can hear it, and what reaches the device is a folder of audio files that is identical every session.

Border control 05

One door, two locks.

Every artifact, dataset, prompt and agent carries a data-class label, and admission policy rejects any workload that omits it. The gateway resolves (class, backend) and refuses illegal pairs with a logged, traced denial. A caller can raise a request's class, never lower it.

callerscitizens: port 8788 only, no keys
the companion, on the host
──▶
model-gatewayrouting · policy
accounting · audit · on the host
──▶
dummy-inference€0 · deterministic
llama.cpp · local 27Bthe laptop's GPU · keyed
web searchSearXNG · public data only
frontier APIoff · policy-gated egress

Lock one is the gateway. Lock two is the network: agent namespaces are default-deny, reaching only Postgres, MinIO, NATS and the gateway's one port. On the laptop, where no pod rule reaches, lock two is an outbound firewall rule for the daemons — Docker lets every container reach the host's loopback, so loopback alone is no lock. An agent that somehow acquires an API key still has no route to use it. Today this is designed; the companion's admission checks are built and tested, and the rest is not built yet.

ClassMeaningMay reach frontier APIs?
publicAlready public, or intended to beyes
internalOurs, unpublished, low harm if leakedyes, with redaction
confidentialOurs, real harm if leakedno — local models only
restrictedHarm to people if leakednever leaves the machine
For that data the difference between a policy and a promise is somebody's safety, so it is enforced at two independent layers on the assumption that either one may fail. Secops · why restricted exists

Budget is a safety control, not accounting hygiene. An agent that loops is functionally a denial-of-wallet attack against its owner. Ceilings per run, per ticket and per day, priced in GPU time, because one laptop decodes about two million tokens a day and that is what is scarce. Three review rounds, then a human decides. The city and the companion have separate budgets, so a runaway agent cannot silence the companion. Stub mode reports realistic token counts, so all of it is tested at €0.

And three independent records, because a single log an attacker can edit is a comfort, not a control: the gateway ledger, the traces, and immutable run records.

Decisions 06

Twelve decisions settled. Ten corrections proposed.

Written down as ADRs so they can be argued with later by someone holding the reasoning, rather than re-litigated from memory. Some already have been: an October review found the design and the repository disagreeing in ten places. Each correction is an ADR of its own, proposed until the principal accepts it, and none rewrites the decision it corrects.

ADRDecisionThe cost accepted
0001k3s as the city substrate, three nodes~1.2 GB and real complexity — single-node hides affinity, eviction and drain bugs until cloud day
0002The OpenAI API shape as the one internal contractVendor-specific features are inexpressible; the gateway grows a typed passthrough when one is genuinely needed
0003NATS JetStream as the city busLess ubiquitous than Kafka — irrelevant for one person, a real cost if that changes
0004Abstract tier labels, never machine names — mechanism corrected in 0013The most consequential decision in the repository. Laptop→GPU becomes a label and a route, not a migration
0005All model traffic through one gateway — one per trust domain since 0014A single point of failure, accepted because it is also the single point of audit
0006Stack chosen for what it teachesComponents that are cheap to run but absent from practice have negative value
0007Inference runs on the host, not in the clusterThe gateway moved with the GPU and left GitOps, so it needs its own provenance
0008A ternary 27B as the first local modelA pinned fork: a stock binary loads the weights and writes fluent nonsense, so verification asserts on meaning
0009Provenance is a field on every messageEvery layer that touches a message must carry it, memory included
0010The tailnet is how applications reach the companionA dependency on Tailscale's control plane
0011The products are the city's first citizensProducts generate unbounded work that feels like learning and is not
0012Three deploy targets; edge products stay at the edgeTwo operating models in one head
ADRCorrection · proposedWhat it fixes
0013One label key per tierA label key holds one value, so one node could not wear two tiers; every agent pod would have stayed Pending
0014The host is its own trust domainDocker lets every container reach the laptop's loopback; the model server now demands a key
0015Provenance is computed from contextA tool call inherits the least trusted words that produced it; the test no longer labels the attack by hand
0016The journal arrives labelledThe companion cannot read Selfwise's journal; entries come inside a request, each marked untrusted
0017Work reaches the human only through a verdictThe scientist could publish straight to results, past the referee
0018Budgets priced in GPU time, in two domainsThe old ceilings measured re-read prompts, not GPU use, and one runaway could silence the companion
0019Records are replayable, not reproducibleA hash cannot replay a run, and a re-run only reproduces on the hardware that was measured
0020kube-router until L7, then CiliumThe config and the docs named different network plugins, and one cannot be swapped live
0021Flux owns the cityTwo GitOps controllers were both described as owning it, and the kill switch would have been undone
0022Selfwise is edge-nativeIts production is a static site and a Worker; the plan to run it in the cluster could not have built it

Where it is 07

Honestly, at the beginning.

The architecture is written and most decisions are made. The cluster is not running yet; one real model is. Publishing the milestone it is actually on is cheaper than explaining a status light that cannot go red.

First come the platform labs the city is built from — secrets, observability, delivery, recovery. M0 follows them.

M0

The city runs. Fake scientists complete a real research loop end to end, watchable in a dashboard.

after the labs
M1

The loop produces something the principal would not have written, and the record proves how it got there.

planned
M2

A real model drops into the foundry, and no citizen's spec changes. It runs and is verified; it had to run on the host, so the gateway moved with it, and that gateway is not built yet.

partly
M3

The city outgrows the laptop and moves to GPUs by editing config.

planned
M4

The city produces work that leaves the building: papers, services, artifacts published here.

planned
A green suite that cannot go red proves nothing. The rule every milestone is held to