SimplifyGenAIMain deck · Technical brief · September 2026How SAGE works, from the runtime up.
This is the deep version of the main deck. It covers the architecture, how context and memory work, what each core agent does, the operating contract every employee follows, the channels, the security model and what a deployment on your infrastructure includes. Every claim points at a file, a command or a setting on the host. The reference deployment described here is SimplifyGenAI's own: one SAGE box (sage-1) running an employee named Trejon, so unit, mailbox and path names carry that employee's name.
Contents.
3 to 5
Architecture
The system map, the engine, and how an employee is invoked.
6 to 9
Context and memory
Retain blocks, the bank, hooks, the nightly curator and the work graph.
10 to 14
Core agents
The ten roles that run the office, and the HQ listener, Inspector and Governor in detail.
15 to 17
Operating contract and channels
The thirteen rules every employee follows, WhatsApp, Telegram and Google Workspace.
18 to 22
Security model
Threat model, layers, privilege, gates and switches, the self-audit.
23 to 26
Host, data, deployment, comparison
What the box looks like, what leaves your cloud, what we set up for you, and how this compares with open-source frameworks.
One engine, one memory, one supervised fleet.
Messages come in at the top and pass through a listener. Work happens in the middle on one runtime. Anything going out passes a code gate at the bottom.
Where people talk
claude -p from systemd units and shell wrappers. Hooks, skills, the memory bank and the work graph sit around it as plain files.Operator laptop
Same repository, same memory, same hooks. Interactive sessions and reviews. Syncs with the server through git push and pull.
Model provider
A Claude subscription seat. Fable, Opus and Sonnet in a chain with cap detection, and a light provider when the seat is capped.
Host
Ubuntu 24.04, 3.7 GB RAM, reachable over a private mesh (Tailscale). Dashboard served through an outbound tunnel.
Six layers, all of them files in a git repository.
Nothing here is a hosted black box. The model is the only thing rented. The same six layers run on the operator's laptop and on the server, from one repository, so the fleet and its owner read from a single source of truth.
claude -p "<prompt>" --model <model> --allowedTools ... with an explicit model and tool list for every run.What happens when a timer fires.
Where the cap regex lives
Every wrapper matches the same pattern: usage limit, limit reached, reached your ... limit, /model to switch, out of usage, credit balance, resets at. A match is a cap, anything else is a bug.
What gets logged
state/last-run.log per employee, append-only jsonl logs in each state directory, governor.log for cap transitions, ops-report.json for health.
What the dashboard sees
org3d/server.py reads systemd, the governor and the transcripts and serves /api/status. It is read-only and never mutates anything.
Memory is written by hooks.
A hosted model keeps nothing between sessions. Here, the agent writes typed facts while it works, a hook files them when the session stops, a nightly job curates them, and the next session starts with the index and the last handover already loaded.
Retain blocks, written during work
- W @Client: contract signed 2026-08-26
- O(c=0.80) @Market: US-first is the right bet
- B @Deploy: build failed on the CDN step
- C @Daksh: approves SEO ships in the group
W is a verified fact, O an opinion with a confidence score, B an experience, C a contact update. Every line carries an @Entity tag, which is the retrieval key across sessions.
The bank
memory/bank/ with world/ for facts, opinions/ for scored judgements, experience/ for dated events, contacts/ one file per person, inbox/ as the landing zone. index.md maps entities to files and is kept under 40 lines because it is injected whole.
Two more stores
state/handover.md carries the last session's mechanical summary and an optional written reflection. Daily memory files decay on purpose; only bank-promoted content lasts.
Hooks run outside the model and can deny a call.
Registered in the workspace's Claude Code settings, gated on the project directory so they never fire in unrelated sessions.
| Hook | Event | What it does | Why it is there |
|---|---|---|---|
| context-loader | SessionStart | Injects bank index, handover, today's memory tail, inbox tails and flags, most important first | The session starts with context instead of a blank page |
| block-secrets | PreToolUse | Denies reads of .env, id_rsa and id_ed25519, .pem, .key, AWS and netrc files; blocks printenv and export dumps; blocks curl or wget with a secret variable; blocks base64-decoded payloads piped to an interpreter | The security floor. Exit 2 denies the call outright |
| content-redact | PostToolUse | Scans tool results for Anthropic and OpenAI keys, GitHub tokens, AWS keys, long bearer tokens and JSON password fields, and warns before they reach the model | A second net if a secret slips past the first |
| agent-model-gate | PreToolUse (Agent) | Denies any subagent launch without an explicit model tier | Cost control; cheap work goes to cheap models |
| discipline-tracker | PostToolUse | Nudges a memory write at 8 tool calls and a handover checkpoint at 15; soft break at 150, hard warning past 200 | Bounds an unattended session |
| pre-compact | PreCompact | Extracts retain, decision and todo markers from the transcript and forces a memory flush before the context window is truncated | Long jobs survive compaction |
| state-persist | Stop | Extracts Retain blocks and decisions, appends to today's memory, routes to the bank inbox, rewrites handover.md, commits and pushes. Never aborts | Persistence with no user action |
| should-sandbox, edit-watcher | PostToolUse, Stop | Advisory only: nudges large outputs to a file, and logs later edits to emitted artifacts with a diff | Token hygiene and an edit trail |
Dreaming: the bank gets tidier while the company sleeps.
At 04:30 a Sonnet run reads the inbox and the index, promotes what deserves a permanent file, merges duplicates, and logs every change. Its prompt scopes it to memory/bank only. It cannot touch identity, state or skills, and it cannot push, message or reach the network; the wrapper script does the commit.
- Conflicting facts are kept side by side and the older one is marked superseded with a date. Nothing verified is deleted for being unused.
- A recall counter scans real transcripts for which bank files get read, so promotion follows use. Low recall can mark a file cold, never delete it.
- Before the commit, dream-guard.sh rejects any deleted curated file and any file that lost more than 40 percent of its lines. A rejection wakes the Inspector.
From the real DREAMS.md log
2026-08-21
promoted: 27 lines into 9 new curated files
merged: 8 duplicate lines
superseded: 1 conflicting fact
left in inbox: 0
2026-08-24
nothing to promote
Where the design comes from
The guard pattern (snapshot before rewrite, keep and cite, a separate diary) follows OpenClaw's published Grounded Dreaming design. The usage-weighted recall signal is ours.
Knowledge in the bank, work in the graph.
Work items live in beads, a dependency-aware issue tracker stored in Dolt and synced through the same git remote. The boundary is a written rule in both directions: identity, contacts and lessons never move into the tracker, and tasks never live in prose.
Session start
bd ready
The hook injects what is unblocked right now, so the employee resumes instead of re-deriving the plan.
On discovery
bd create
Found work is filed the moment it turns up, with a priority and a reason.
Dependencies
bd dep add
Blockers and epics are edges. The tracker knows what is waiting on a human.
Every stop
snapshot and push
Issues are exported to a diffable jsonl file and the Dolt data is pushed, so the history survives any one machine.
Ten core roles.
always on
Trejon
The HQ listener. Full-agency runs for named owners, replies with results.
every 30 min
Inspector
Fleet health watchdog and the target of every failure hook.
every 10 min
Governor
Detects usage caps, flips the fleet tier, probes for recovery.
before each run
Budget keeper
24-hour output-token caps per employee and per fleet.
04:30
Dreamer
Nightly memory curation under the shrink guard.
Sunday 08:00
Caretaker
Log truncation, digest cleanup, weekly Drive link sweep.
always on
Switchboard
The WhatsApp sync daemon; downloads media for every employee.
every 3 min
Secretary
Triages the trejon@ inbox and keeps the CRM sheet.
4 shifts
Standup crew
Morning, midday, end of day and night digests.
Sunday 05:30
Sensei
Reviews the week's transcripts and proposes skill improvements.
All ten are rows in roster.json with a unit, a model, a memory cap and an OnFailure escalation. The dashboard flags any timer that is not on the roster.
Trejon, the boss employee of this deployment.
hq-listener.py v3 runs as trejon-hq.service with Restart=always. It reads the HQ WhatsApp group through the local sync database and turns messages from named owners into full-agency Claude runs on the box.
Result-only replies
A command runs on a worker thread. The reply is what the work produced, checked against logs. A heartbeat "on it" only appears if the job passes 75 seconds.
Trust tiers
Two named senders map to a full-tools run. Anyone else in a team group gets a read-only Sonnet run with Read, Glob and Grep. Messages the agent wrote itself carry a prefix and are skipped, so it cannot loop on its own output.
Media is mandatory
The sync daemon downloads media. wait_media() polls the database row for the local file path for up to 25 seconds, transcribe_audio() handles voice notes, and the path goes into the prompt so the model reads the file natively.
Light mode
When the seat is capped, the same claude binary is pointed at a free provider through ANTHROPIC_BASE_URL, with read-only tools and an instruction never to claim work it cannot do.
Deferred restart
When it changes its own code it compiles the file, touches hq/RESTART-PENDING and exits when idle. systemd brings it back on the new code. It never kills a run in progress.
Send gate
A send needs the destination in send-allowlist.yaml and the standup/ENABLED flag to exist. The check is in the send function, not in the prompt.
The Inspector.
- Runs every 30 minutes as ops-sentinel.timer, and immediately whenever one of about 20 units fails, through an OnFailure=ops-sentinel.service drop-in.
- Checks failed systemd units, roster employees whose last run failed, enabled timers with no next run, a governor stuck degraded for more than 6 hours, disk above 85 percent, available RAM under 300 MB, and the dashboard API.
- Writes ops-report.json and announces to the HQ group only on a state change, at most once an hour, with a three-line diagnosis from a read-only Sonnet run, or a canned line if the seat is capped.
- Read-only apart from its own state directory. It can see everything and change nothing.

The Governor and the Budget keeper.
budgets.json
window_h 24, default_out_cap 500,000, fleet_out_cap 6,000,000, and per-employee overrides between 300,000 and 3,000,000 output tokens. Sized at roughly three times observed peaks, so only a runaway trips.
budget.py check
Called in the shell wrapper before claude starts. Exit 3 means over budget and the run is skipped. Any internal error exits 0, on purpose, so a broken sentinel can never stop the fleet. State refreshes every 10 minutes from transcript tails; it is an estimate, not billing.
Live at capture
Fleet at 3.56 million of 6 million output tokens in the trailing 24 hours. The HQ listener at 55 percent of its 3 million cap, the SEO engine at 57 percent of 2.5 million. No employee over budget.
Which model, and how much memory.
Memory caps on every AI unit
MemoryHigh 1600M and MemoryMax 2000M in a systemd drop-in, up to 2400M for the heaviest run. Enforced by the kernel's cgroup controller. Bot units get much smaller caps sized to the job, down to 48M for the public feed server.
Restart policy
Long-running services use Restart=always with a 10 to 15 second delay. Timers use Persistent=true so a missed run catches up after downtime.
The operating contract, in thirteen sections.
AGENT-GUIDELINES.md is the checklist any session deploying an employee follows top to bottom. The self-audit script checks the host against it.
| Section | Rule | Section | Rule |
|---|---|---|---|
| s1 Unit | One systemd unit per employee, named by product, with an EnvironmentFile for secrets and its own working directory | s8 Trust check | Pin exact versions, vet the publisher, read install hooks, prefer official registries, audit after install |
| s2 Models | Sonnet by default, the Fable to Opus to Sonnet chain for flagship runs, the shared cap regex, governor checks before heavy work | s9 Budgets | Per-employee 24-hour token caps, checked before dispatch |
| s3 Roster | Every employee is a row in roster.json with id, role, org, unit, type and model. Unregistered timers are a bug | s10 Media | Any employee that reads a human channel must handle voice, images, video and documents. Deferred restart pattern |
| s4 Messaging | Never send to anyone outside the allowlist, and only while the ENABLED flag exists | s11 Email format | Every outbound body passes the formatter before the approval check |
| s5 Hygiene | Idempotent runs, state in the employee's own directory, append-only capped logs, no secrets in code or logs | s12 Links | Every Google link is run through the share check before it leaves |
| s6 Reliability | Smoke test by hand before enabling a timer, OnFailure to the Inspector, exit code convention | s13 Design | Visual output loads the design skills and passes a pre-flight |
| s7 Memory caps | MemoryHigh and MemoryMax on any unit that runs claude |
Inbound is open. Outbound is 24 destinations.
wacli 0.15.2 runs as wacli-sync.service with media download on. Employees read from its local SQLite store. Sending is a code path with two conditions and no model in the loop.
Allowlist
send-allowlist.yaml holds 24 approved destinations, each with a comment on its role. Client-facing groups are deliberately excluded. The same file is read by the listener, the governor and every messaging employee.
Group per role
The SEO engine posts only to the SEO group, the outbound desk only to the outreach group, the leads digest only to the BD group. Each employee's settings scope its send permission to its own group id.
Approval patterns
The outbound desk waits for an explicit go on each send batch. Production deploys wait for the marketing owner's written go in the group. The leads digest holds any batch with fewer than two verified leads.
Telegram
The Coach runs on the official Bot API with no third-party dependencies. It sends to a single private chat pinned the first time the operator writes to it, re-checked mechanically on every send, and it refuses any other chat id even if its state file is edited.
Client channels
Customer-facing deployments use the WhatsApp Business API or Slack and Teams apps. The self-hosted sync client stays for internal operations only.
Voice both ways
Outbound voice uses edge-tts through a shared script, transcoded to Opus. Inbound voice notes are transcribed locally. If the voice path fails, the text still goes.
trejon@simplifygenai.com has exactly five permissions.
The employee holds its own Workspace identity through a delegated service account with domain-wide delegation. Each scope was granted on its own and tested end to end from both machines.
drive.file covers files the app itself created or opened, not the company Drive. There is no Docs scope and no admin scope.
The threat model.
An agent becomes exploitable when three things meet: access to private data, exposure to untrusted content, and a channel to send things out. Every employee here has the first two by definition, because that is the job. So the control sits on the third: nothing goes out unless code says it can.
- OWASP's 2026 list names prompt injection (LLM01), excessive agency (LLM03), supply chain (LLM04) and unbounded consumption (LLM06). The gates, tool scoping, pinned skills and budgets map onto those four directly.
- Anthropic's own data shows people approve about 93 percent of permission prompts regardless of content. So approvals here attach to destinations and content, not to every keystroke.
Eight layers, none of them a prompt.
Read from the outside in. Each one can be verified with a file path or a command on the host.
Permissions attach to the run, not to what the model believes.
| Who or what | Model | Tools | Can it send? | Set by |
|---|---|---|---|---|
| Named owners in HQ | Opus, Sonnet as fallback | Full tools on the host | Only to the 24 approved destinations | A sender map in the listener and the allowlist in the send function |
| Team members in a group | Sonnet | Read, Glob, Grep only | Into that group | A fixed --allowedTools list on the command line |
| Unknown number | none | none | no | Not matched, so never processed |
| Degraded mode | A light provider | Read, Glob, Grep | Status only | An environment override in the wrapper |
| Scheduled employee | As set in roster.json | As set in its unit and settings | Its own group only | The systemd unit and per-employee settings |
| Nightly curator | Sonnet | Edit inside memory/bank | No network, no git, no messages | The workflow scope and the memory guard |
| Weekly caretaker | Sonnet | Bash, Read, Write, Glob, Grep | One exception report to HQ | The unit's tool list |
Before anything goes out.
Stopping things.
Quiet
Remove the ENABLED flag file. All outbound messaging stops fleet-wide.
Step down
Set the governor tier to degraded. Heavy runs skip until a probe clears it.
Starve
Lower one budget in budgets.json. The wrapper skips the next run within ten minutes.
Stop
systemctl stop on any of the 52 units. There is no hidden runtime.
Email is default deny: with no approval token present, nothing leaves. The memory guard exits non-zero and blocks the nightly commit if a rewrite would shrink the bank.
The self-audit, and what gets installed.
security-audit.sh is a read-only script that prints PASS, WARN or FAIL per check and exits non-zero on any FAIL. It runs after any install and before a new employee goes live. The latest run returned 0 FAIL.
1 Network exposure
Lists every listening socket and flags any bind that is not loopback or the mesh interface.
2 Send guard
Confirms the allowlist file and the ENABLED flag exist and are intact.
3 Secret file modes
Requires 600 or 400 on the environment file, the dashboard viewer key and the SSH keys.
4 Fleet compliance
Cross-checks every AI row in the roster against live systemd for MemoryMax and OnFailure, and checks every timer on the box traces back to a roster row.
5 Third-party inventory
Lists installed skills and plugins so the footprint is enumerable and reviewable.
6 Budget report
Confirms the budget keeper runs cleanly and prints the per-employee state.
What the host looks like.
The whole office runs on one small virtual machine. A client deployment uses the same layout on a host in your cloud.
| Operating system | Ubuntu 24.04.4 LTS |
| Memory and disk | 3.7 GB RAM, 38 GB disk with 14 GB free at capture |
| Uptime at capture | 29 days, load average about 1.4 |
| Runtime | Claude Code CLI 2.1.197, Node 20, Python 3.12, wacli 0.15.2 |
| Network | Tailscale mesh with two peers; dashboard on port 8940 through a Cloudflare tunnel; the internal trading dashboard bound to the mesh address only |
| Secrets | .claude-env, the dashboard viewer key and the deploy key at mode 600, root-owned, referenced only through EnvironmentFile |
| State | One directory per employee under /root/<employee>/state with append-only logs; the shared memory bank at /root/trejon/memory/bank, committed nightly |
| Other workloads | Two Docker containers for a mailing list tool and a rank tracker, bound to loopback only |
| Backups | trejon-backup.timer nightly at 03:30; the repository itself is on a remote |
Data flows.
Retention.
Logs
Append-only per employee in its state directory. Two high-volume files are truncated to their last 2 MB weekly by the Caretaker. OS logs rotate weekly.
Memory
Committed to git nightly. Every prior version is in the history. The curator marks facts superseded instead of deleting them.
Approvals
Email approvals are single use and expire after 24 hours. The governor log keeps every tier change. The Inspector keeps its last report and last announcement.
Media
Voice notes, images and documents from chat are downloaded to the host by the sync daemon and referenced by path. They stay on the host.
Enterprise deployments add SIEM export with 90-day retention and a secrets manager, per the baseline on the next page.
The baseline we deploy with.
Everything on the running fleet, plus the controls a security review expects. We walk your team through the evidence before the first employee goes live.
You keep everything
Identity, memory, schedules and rules are files in a repository you own. A bootstrap script restores a machine from a clone: dependencies, the work graph, the schedules, the plugins. Secrets are restored by hand, on purpose. The first session on a new machine runs the onboarding interview.
Network
Host firewall on, mesh interface only, no public inbound ports, dashboards behind authenticated tunnels.
Isolation
One service user or one container per employee, with the same memory caps and failure hooks.
Secrets
Your vault (HashiCorp, AWS or GCP) with rotation, and short-lived tokens where the provider allows.
Audit trail
Structured, tamper-evident logs exported to your SIEM and kept for 90 days.
Inbound scanning
Injection and exfiltration pattern checks on channel text and on memory writes, on top of the code gates.
Channels and assurance
WhatsApp Business API, Slack or Teams apps for anything customer-facing. A third-party penetration test on the deployed host, and a SOC 2 path if procurement asks.
Compared with the open-source frameworks.
| Area | OpenClaw 2.0 | Hermes Agent | SAGE |
|---|---|---|---|
| Spend caps per agent | ○ Requested March 2026, draft PR still blocked on review | ○ No hard cap. Its estimator under-counted spend by 3 to 5x (issue 94408) | ● Budget checked before each run, per employee and per fleet |
| Third-party code | ○ Open marketplace. 1,184 malicious packages in one campaign, CVE-2026-25253 | ◐ Skills ecosystem with a write-approval queue | ● No marketplace. Pinned skills, audit after install |
| Memory tidying | ● Grounded Dreaming | ◐ Capped files, manual review | ● Same guard pattern, plus recall-weighted curation |
| Work tracking | ○ None apart from memory | ◐ Cron tasks and a queue | ● Dependency graph, synced through git |
| Fleet operations | ◐ Workspace per agent | ● Bot mode and a scheduler | ● Roster, timers, watchdog, failure hooks, memory caps |
| Outbound approval | ◐ A config setting | ● Approval modes and a blocklist | ● Destination and content gates in code |
| Google Workspace | ◐ Google Chat channel | ◐ Email channel | ● Delegated service account, five named scopes |
| Sandboxing | ◐ Docker sandbox, off by default | ● Seven execution backends | ◐ Cgroup caps today, containers in the deployment baseline |
● shipped ◐ partial ○ missing. Sources: openclaw/openclaw issue 42475 and PR 104060, docs.openclaw.ai, NousResearch/hermes-agent issues 94222 and 94408, Unit 42 and Koi Security research, all read on 2 September 2026. The memory guard design and the audit-script idea came from OpenClaw. Reviewer questions go to trejon@simplifygenai.com and get an answer from the operator, not from an agent.