Toilville LLC — Forge Reference
Reference for forge operators (Peter + future sysadmin). Covers infrastructure internals that teammates do not need to know.
See deploy/README.md for the full authoritative stack
list and layering.
Quick summary: - core.yml — always running: postgres,
redis, redpanda, openbao - ritual.yml — always running:
ritual-server, ritual-consumer, ceremony-warden -
platform.yml — always running: keycloak, forgejo, matrix -
observability.yml — always running: grafana, prometheus,
dozzle - apps.yml — always running: forge-bridge,
slack-bridge, audit-events, lore-drift, git-poller
Legacy stacks (docker-compose.yml,
docker-compose.prod-full.yml) are deprecated — do not use
for new deployments.
bin/forge-deploy-check # report drift between tracked files and live host
bin/forge-deploy-check --sync # apply tracked files to host| Context | Broker | Protocol |
|---|---|---|
| External/TLS clients (daemons, scripts, hooks) | spel.toilville.dev:19092 |
SASL_SSL |
Internal Docker containers (on forge-network) |
redpanda:9092 |
plaintext |
Never use 10.10.0.1:19092 for TLS
clients. The Redpanda TLS cert is issued for
spel.toilville.dev. IP addresses break hostname
validation.
The host firewall must also allow inbound 19092/tcp on
toilville-forge. That rule is persisted via
netfilter-persistent and is required for the public Kafka
listener to survive reboots.
SASL credentials: secret/forge/kafka-scram in
OpenBao.
Validation:
bin/forge-kafka-check # fail if any TLS client uses an IP brokerhttp://172.22.0.1:8200http://10.10.0.1:8200https://vault.toilville.dev (via nginx
proxy → 10.10.0.1)openbao on
toilville-forgeThe canonical mapping of service → vault path is in
forge-manifest.spel under
infrastructure.secret_paths. Key paths:
| Service | Vault Path |
|---|---|
| kafka SCRAM credentials | secret/forge/kafka-scram |
| ritual-server | secret/forge/ritual-server |
| audit-events | secret/forge/audit-events |
| lore-guards | secret/forge/lore-guards |
| forge-bridge | secret/forge/forge-bridge |
| git-poller | secret/forge/git-poller |
| Woodpecker CI | secret/forge/woodpecker |
Each service has a sidecar in
deploy/vault-agent/<service>/: -
agent.hcl — AppRole auth + template sink -
secrets.env.tpl — renders secrets to
/vault-secrets/secrets.env
Vault address in sidecars is http://172.22.0.1:8200
(Docker gateway IP).
Claude hooks are registered in ~/.claude/settings.json:
- PreToolUse: bin/claude_pre_tool — Bastia
grant gate + audit event - UserPromptSubmit:
generated/hooks/claude_pre_prompt.sh — context injection -
SessionStart:
generated/hooks/claude_session_init.sh — env injection +
permission sync
The generated/hooks/ files are generated from
bin/templates/hook_wrapper.sh.tmpl. Do not edit generated
files by hand — edit the template and regenerate:
bin/forge-regen-hooks
git add generated/hooks/
git commit -m "regen: hooks from canonical template"All hooks fail open (exit 0) by default. The only blocking behavior
is the Bastia grant gate in claude_pre_tool for Z2/Z3
operations (vault writes, psql mutations, remote writes, paladin frame
ops, ssh/scp to production).
Local-only mode (set at session init via
RITUAL_LOCAL_FIRST=1): - Pre-prompt hook skips Kafka/server
and serves from cr-sqlite cache - Cache freshness is emitted to stderr:
[forge-local] cache age: Xs, tier: N
Managed in core.bastia_grant_scopes and
core.bastia_grant_policies.
| Scope | Policy | Auto-Approve | Max TTL |
|---|---|---|---|
vault:read |
bastia-grant-vault-read | Yes | 60s |
vault:write |
bastia-grant-vault-write | No (SLA) | 60s |
db:query |
bastia-grant-db-query | Yes | 120s |
paladin:exec |
bastia-grant-paladin-exec | No (SLA) | 120s |
remote:write |
bastia-grant-remote-write | No (SLA) | 60s |
service:restart |
bastia-grant-service-restart | No (SLA) | 60s |
secret:patch |
bastia-grant-secret-patch | No (SLA) | 60s |
forgejo:credential-rotate |
bastia-grant-forgejo-credential-rotate | Yes | 120s |
kafka:delegated |
bastia-grant-kafka-delegated | — | 900s |
Narrow helpers in bin/forge-privileged-op wrap the
common operations. Do not add raw bao, ssh, or
scp to Claude’s allowlist — use bounded helpers
instead.
bin/forge-privileged-op vault-read <path>
bin/forge-privileged-op vault-write <path> <json>
bin/forge-privileged-op remote-copy <local> <host:path>
bin/forge-privileged-op remote-restart <service>
bin/forge-privileged-op service-restart <service>
bin/forge-privileged-op secret-patch <path> <field> <value>
bin/forge-privileged-op db-exec <sql-file>
bin/forge-privileged-op rotate-forge-ci-passwordEach operation: requests grant → executes → revokes. Fail-closed if Bastia denies.
cr-sqlite cache lives at ~/.ritual/schema.db. Freshness
tiers:
| Tier | Condition | Behavior |
|---|---|---|
| 0 | < 5 min old | Fresh — Kafka not needed for reads |
| 1 | 5 min – 1 hr | Stale — reads may lag |
| 2 | > 1 hr | Very stale — consider sync |
| 3 | Missing | No local cache — server required |
bin/forge-local-cache-check # human-readable report
bin/forge-local-cache-check --json # machine-readablebin/ritual-system-doctor # run all checks
bin/forge-kafka-check # Kafka policy validation
bin/forge-deploy-check # deploy drift check
bin/forge-local-cache-check # cache freshnessYou don’t need to learn Spelwork, ritual, Kafka, or Docker to contribute. This guide explains the human workflows.
Send an email to the Toilville forge inbox. Write your request in plain English in the subject line. The forge reads it, creates a work item, and routes it for review.
Examples of things you can send: - Review the latest design
mockup
- Can you approve the Q2 budget estimate?
- Update
the sprint goal for this week
- Help — the integration is broken
again
You’ll receive a reply if clarification is needed. Decisions and outcomes are recorded automatically.
Open a pull request or issue in the Forgejo
repository at git.toilville.dev. The forge monitors all
repos in the toilville organization and creates work items
automatically when you:
You don’t need to mention the forge or use any special syntax. Just work normally in git.
When a request requires a group decision, you may receive a decision parlay — a structured prompt asking you to vote, object, or add context.
What did we decide about X?
If you need something restarted, redeployed, or updated on the forge infrastructure, send an email or open an issue describing what you need and why. The request is routed for approval before anything runs.
Do not ask for raw access to production servers. Bounded operation requests are the correct path.
These are implementation details — you do not need to understand them to contribute:
If you find yourself needing these, ask an operator — it likely means there’s a missing bounded helper or form that should exist.
Send an email with your question. Or open a Forgejo issue in
toilville/spelwork tagged help.
No services registered.
No migrations recorded.
No events cataloged.
No intents cataloged.
No frames recorded.
No compliance rules configured.
Lore contributed by external repos via
POST /familiar/lore/ingest.
No cross-repo lore contributed yet.