System ArchitectureField Brief  Nº 005

How LiquidSilicon is built.

One codebase serves every vertical. At the center is an event-sourced core — every write is an append, folded into CQRS projections and a business knowledge graph, replayable to any past state. The runtime is config-driven: a tenant’s objects, fields, and views are versioned data, not per-vertical code. Over that stream, five specialized AI agents observe, advise, build, guard, and learn — always behind mandatory human gates. The rest of this brief is each of those pieces, with the file that implements it.

N01Event-sourced

append-only log, replayable to any past state

N02Five agents

observe · advise · build · guard · learn, human-gated

N0323 tables

one PostgreSQL, RLS on 21 of them

N0411 containers

one Hetzner AX102, host-level Caddy TLS

LIQUIDNUCLEATIONCRYSTALLINE LATTICE
Fig. 1The thesis in one plate: a liquid description nucleating into a fixed lattice — fluid where the business is still uncertain, structured where it has settled. Everything below is how that structure is engineered.
01 / THE DATA SPINE

Every write is an append. Nothing is overwritten.

Subsystem 01 · Event-sourced core Shipped

Every domain write funnels through a single helper, emitEvent(), so the fold-out into read models is identical everywhere. It appends the row to the append-only events table — the source of truth, never UPDATEd or DELETEd — then folds it synchronously into the CQRS projections and, fire-and-forget, into the knowledge graph and the Observer trigger. Because the log is the truth, any projection can be rebuilt by deterministic replay: POST /api/events/replay re-reduces the stream in(created_at, id) order, and projection_checkpoints stores the high-water mark so replay is resumable and drift is self-healing.

APPEND-ONLY EVENT LOG← LATESTE1E2E3E4E5E6E7REBUILDPROJECTION · ORDERSPROJECTION · REVENUEREPLAY FROM ANY POINT
Fig. 2The append-only ledger folding forward into its projections, with one checkpointed replay path lit — the same log is the audit trail and the time machine.
write: src/lib/events/emit.tsreplay: src/lib/events/engine.tssrc/lib/events/projections.tssrc/db/schema-projections.ts

The synchronous projection fold is wrapped in try/catch: a projection bug can never fail an event write. Drift is repaired by replay, not by blocking the append.

Four projections are derived, deterministic, and safe to TRUNCATE: entity_activity, agent_activity, tenant_event_stats, and the projection_checkpoints replay marker. Nothing writes there except the engine.

02 / THE KNOWLEDGE GRAPH

The shared truth the agents reason over.

Subsystem 02 · kg_entities / kg_relationships Shipped

The event stream is projected — async, fire-and-forget — into kg_entities and kg_relationships. Entity resolution keys on (tenant_id, entity_type, natural_key), so the same business object seen across many events resolves to one row via upsert-merge. Every node carries source_event_id for provenance back to the log and a confidence score, so the graph can hold uncertain, AI-derived facts alongside hard ones. Traversal is a recursive CTE bounded at MAX_DEPTH = 5 — deep enough to reason across a business, bounded so a query can’t walk forever.

d1d2d3d4d5ROOTLEAFTRAVERSAL PATHMAX DEPTH 5
Fig. 3The graph drawn as a depth-layered lattice with one traversal path lit — entities resolved from many events into single nodes, bounded five hops deep.
src/lib/knowledge/graph.tssrc/lib/knowledge/ingest.tssrc/db/schema-graph.ts
03 / THE CONFIG-DRIVEN RUNTIME

Your vertical is data, not code.

Subsystem 03 · tenant_configs → records API → engine Shipped

There is no per-object table and no per-vertical codebase. A tenant’s objects, fields, stages, and views live in tenant_configs — a versioned TenantConfig where one active version drives the whole rendering engine and older versions are retained for rollback. The generic records API at /api/records/[objectKey] reads and writes kg_entities by key, and the engine renders table, kanban, or detail per the active config. Onboarding is either 10 templates or an AI-describe flow that generates the config from a plain-language description of the business.

TENANT APPYOUR OPERATION, DESCRIBEDSTANDARD PORTSMCPAPIEVENTSAGENT CLIENTCONNECTEDAGENT CLIENTvia APIAGENT CLIENTvia EVENTSONE BOARD · STANDARD PLUGS
Fig. 4Your described operation as a board of standard plugs — API, events, and (on the roadmap) a per-tenant MCP endpoint — that the runtime and the agents connect into.
src/app/api/records/[objectKey]/route.tssrc/lib/ai/config-generator.tssrc/db/schema.ts · tenant_configs

The wedge (franchise, CRM, …) was removed as code in the de-wedge refactor (migration 005); those objects now live as tenant data in kg_entities. Change the description and the runtime reshapes — no rebuild, no migration project.

A per-tenant MCP endpoint, exposing each tenant’s objects to external AI agents over the Model Context Protocol, is a roadmap item. Designed Today the standard plugs are the records API and the event bus.

04 / THE FIVE AGENTS

One job each. One loop.

Subsystem 04 · the agent loop Shipped

There is no single opaque “AI” — five narrow agents each have one job. What runs on the live loop today is the Observer cycle: ObserverAutoTrigger fires on a 50-event threshold or a 5-minute timer per tenant (whichever comes first) and runs an analysis pass — detect patterns, enhance them with an AI call, and write the insights that become the decisions in your briefing. A person acts on each one, and those outcomes feed trust-tier advancement.

The fully chained Observer → Advisor → [human] → Builder → [human] → Guardian → Learner pipeline, with per-action gating on each agent’s trust tier, is built in the codebase but not yet wired into that live loop. Designed

OBS-01OBSERVERWatches everyevent stream.ADV-02ADVISORRanks & coststhe options.BLD-03BUILDERWrites theapproved change.GRD-04GUARDIANVetoes anythingunsafe.LRN-05LEARNERFeeds outcomesback in.REGISTER OF AGENTS · N05ONE APPROVAL GATE
Fig. 5The five agents as a specimen sheet — eye, compass, pen-nib, shield, and cycle — each drawn in the same hand, each with the one job it is allowed to do.
OBS-01ObserverWatches the event stream, detects patterns and anomalies, and kicks the loop. Reads events, never writes.event-processor.tsReads. Auto-triggered.
ADV-02AdvisorTurns an observation into a ranked, costed recommendation with a confidence score. Recommends; cannot act.advisor-pipeline.tsRecommends only.
BLD-03BuilderGenerates the actual change — schema, logic, interface — inside an isolated sandbox, but only ever as a proposal.builder-pipeline.tsProposals only.
GRD-04GuardianScans every output for PII, prompt injection, and permission-boundary breach, and blocks critical and high severity.guardian-pipeline.tsScans and vetoes.
LRN-05LearnerFeeds the real outcome of each shipped change back into the loop so the next recommendation is graded against reality.learner-pipeline.tsCloses the loop.
05 / THE HUMAN GATES

Nothing the AI proposes ships on its own.

Subsystem 05 · two mandatory gates Shipped

There are two mandatory human gates — after Advisor and after Builder — and nothing the AI proposes reaches deploy without a human yes. The Builder never auto-applies: it generates code and config in an isolated sandbox, and its pipeline triggers only on anapproval_granted event. Builder output needs dual control: the human gate and the trust-tier gate must both clear. Approve, and the change ships and is recorded as an event; reject or send back, and the proposal returns to the agent with your note — nothing was applied while it waited.

AGENTPROPOSALOUTPUTSHIPSHUMANGATEPROPOSEAPPROVE ✓REJECT · SENT BACKNOTHING SHIPS WITHOUT A HUMAN YES
Fig. 6The approval circuit. A sandboxed proposal meets the human gate: approve writes an event and ships; reject loops it back to the agent for revision.
src/lib/ai/builder-pipeline.tssrc/lib/ai/builder-sandbox.tstrigger: approval_granted event
06 / THE GUARDIAN

A scan that can block critical findings.

Subsystem 06 · guardian-pipeline Shipped

Guardian’s scan reads an agent output for PII exposure, prompt injection, and permission-boundary breach, deterministic-first (regex before any AI call). On severitycritical or high it can block the change; lower severities it flags. Today it runs through its own endpoint and is gated by GUARDIAN_AUTO_ENABLED (off by default). Wiring it to screen every proposal automatically — an inline gate no raised trust tier can remove — is built but not yet on the live path. Designed

GUARDIANPRE-DEPLOY SCANPROPOSALS →PASSPASSPIIHALTEDPASSPASSVETO BEFORE DEPLOY
Fig. 7The Guardian screen as a shield over the ship path — critical and high severity stopped at the boundary, everything else flagged and passed through for review.
blocks critical/high: src/lib/ai/guardian-pipeline.tssrc/lib/ai/prompt-cleaner.tssrc/lib/ai/privacy-engine.ts
07 / TRUST TIERS

Autonomy is a dial, on its own axis.

Subsystem 07 · trust-engine Shipped

The trust_tier enum runs observe → suggest → automate → expand → replace, set per class of change and evaluated from the Observer cycle. Advancement is earned: it needs human-rating samples above a minimum average, a 14-day cooldown, and passing adversarial tests; failures and Guardian blocks demote. Crucially, this is not the billing axis — trust tier governs autonomy and is entirely separate from the SubscriptionTier system (free / starter / pro / enterprise) that gates features and usage. IT can override, demote, or emergency-freeze any agent to observe-only in one action, no redeploy.

OBSERVE00SUGGEST01AUTOMATE02EXPAND03REPLACE04CURRENTCOOL · OBSERVE-ONLYHOT · FULL AUTONOMY
Fig. 8Autonomy as a laboratory gauge, graduated observe → suggest → automate → expand → replace, with the needle on the current tier — independent of the plan you pay for.
01
Observe

Agents watch and report. Nothing acts.

02
Suggest

Ranked, costed recommendations. No changes made.

03
Automate

Approved change classes run on their own — Guardian still watching.

04
Expand

Autonomy widens to adjacent decisions in the same class.

05
Replace Designed

The agent owns the workflow end to end. Exists in the enum; not exercised in production.

observe · suggest · automate · expand are shipped. The fully-autonomous replace tier exists in the enum but is not exercised in production — it is a designed ceiling, not a live default.

08 / TWO SURFACES, ONE LOOP

Business Console and Control Plane.

Subsystem 08 · the operator loop Shipped

The same change travels one circuit seen from two sides. A plain-language question in the Business Console becomes a proposal an agent makes; an IT reviewer approves it in the Control Plane before it ships. Two audiences, two screens, one loop — ask, propose, approve, ship — with the five agents sitting between the surfaces and the event log recording every hop.

ASKPROPOSEAPPROVESHIPBUSINESS CONSOLEDECIDEMORNING BRIEFINGCONTROL PLANEAPPROVEREVIEW · APPROVE · WATCHAGENTS ×5ONE LOOP · SEEN FROM BOTH SIDES
Fig. 9The loop in miniature: Business Console and Control Plane with the five agents between them, and the four arrows — ask, propose, approve, ship — that carry every change around the circuit.
09 / WHY EVOLVE BEATS REPLACE

The log is why it compounds.

Subsystem 09 · the compounding argument

Rip-and-replace throws away history every few years — the assumptions, the edge cases, the reasons a thing was built the way it was. LiquidSilicon keeps all of it: because the core is an append-only log, nothing is lost and everything replays. Each shipped change is graded by the Learner against what actually happened, and that outcome sharpens the next proposal. The software does not reset to zero every three years; it compounds — every prior decision is still on record, still queryable, still feeding the loop.

YR 0YR 3YR 6YR 9YR 12VALUE ↑TIME →RESET EVERY ~3 YRSCOMPOUNDSRIP & REPLACEEVOLVE
Fig. 10Two paths from the same starting point: rip-and-replace sawtoothing back to zero each cycle, versus a single evolving line that keeps its history and compounds.
10 / RUNTIME TOPOLOGY & SECURITY

Eleven containers, one server, honest isolation.

Subsystem 10 · deployment & data isolation Shipped

The platform runs as 11 Docker Compose containers on a single Hetzner AX102 (Ryzen 9 7950X3D · 192GB · 3×1.92TB NVMe). Cloudflare DNS points at host-level Caddy — not a container — which terminates TLS and sets security headers in front of adaptive-app:3456.

Container topology — Cloudflare → host Caddy (TLS) → adaptive-app:3456
ContainerRolePort
adaptive-appNext.js 15 / React 19 — the app:3456
adaptive-dbPostgreSQL — pg16 prod / pg17 dev5432
redisCache + rate-limit6379
natsJetStream event bus4222
prometheusMetrics scrape + store9090
grafanaDashboards — loopback only127.0.0.1:3301
alertmanagerAlert routing9093
nats-exporterNATS / JetStream metrics exporter7777
postgres-exporterDatabase metrics exporter9187
redis-exporterCache metrics exporter9121
maddySMTP — transactional mail25 / 587

Prod / dev divergences to remember: PostgreSQL is 16 in prod, 17 in dev — reproduce prod behavior with care. The app is pinned to dev port 3456 from the shared port registry. Grafana is loopback-only (127.0.0.1:3301), never publicly exposed.

Row-level security· 21 tables

RLS enabled + a tenant-isolation policy keyed on the app.current_tenant_id session variable (migrations 001–006). tenant_ai_config and ai_spend_ledger were brought under RLS in the 006 BYOK-hardening pass.

  • users
  • activities
  • documents
  • events
  • ai_insights
  • trust_state
  • entity_activity
  • agent_activity
  • tenant_event_stats
  • kg_entities
  • kg_relationships
  • tenant_configs
  • tenant_ai_config
  • ai_spend_ledger
  • notifications
  • team_invites
  • subscriptions
  • features
  • feature_transitions
  • feature_comments
  • feature_checkpoints
App-level filtering only· 2 tables

Not DB-enforced — these depend on a correct WHERE tenant_id = … in the service layer. tenants is the tenant table itself; projection_checkpoints is a global replay marker keyed by projection name, not tenant.

  • tenants
  • projection_checkpoints

On the roadmap, not in code Designed — runtime schema evolution (agents mutating a tenant’s active config live), a 24/7 autonomous agent swarm, generative agent-synthesised UI, and the per-tenant MCP endpoints. Configs today are versioned and swapped deliberately; the renderer is config-driven, not generative.

Read how it works, then start it in observe-only.

The architecture is the machine; the product brief is what it feels like to run. Point LiquidSilicon at your operation, watch the agents work with zero autonomy granted, and raise the dial only when they have earned it.

Observe-only is free · Humans approve every change