Compare commits
13 commits
fix/issue-
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a467d613a4 | |||
|
|
2fd5bf2192 | ||
| 3fb2de4a8a | |||
|
|
c24d204b0f | ||
| 58a4ce4e0c | |||
|
|
b475f99873 | ||
| b05a31197c | |||
|
|
e6dcad143d | ||
| a35d6e7848 | |||
|
|
38b55e1855 | ||
|
|
4f5e546c42 | ||
| 85969ad42d | |||
|
|
31e2f63f1b |
19 changed files with 96 additions and 45 deletions
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# Disinto — Agent Instructions
|
||||
|
||||
## What this repo is
|
||||
|
|
@ -39,7 +39,7 @@ disinto/ (code repo)
|
|||
│ hooks/ — Claude Code session hooks (on-compact-reinject, on-idle-stop, on-phase-change, on-pretooluse-guard, on-session-end, on-stop-failure)
|
||||
│ init/nomad/ — cluster-up.sh, install.sh, vault-init.sh, lib-systemd.sh (Nomad+Vault Step 0 installers, #821-#825); wp-oauth-register.sh (Forgejo OAuth2 app + Vault KV seeder for Woodpecker, S3.3); deploy.sh (dependency-ordered Nomad job deploy + health-wait, S4)
|
||||
├── nomad/ server.hcl, client.hcl (allow_privileged for woodpecker-agent, S3-fix-5), vault.hcl — HCL configs deployed to /etc/nomad.d/ and /etc/vault.d/ by lib/init/nomad/cluster-up.sh
|
||||
│ jobs/ — Nomad jobspecs: forgejo.hcl (Vault secrets via template, S2.4); woodpecker-server.hcl + woodpecker-agent.hcl (host-net, docker.sock, Vault KV, S3.1-S3.2); agents.hcl (7 roles, llama, Vault-templated bot tokens, S4.1); vault-runner.hcl (parameterized batch dispatch, S5.3); staging.hcl (Caddy file-server, S5.2); chat.hcl (Claude chat UI, Vault OAuth secrets, S5.2); edge.hcl (Caddy proxy + dispatcher sidecar, S5.1)
|
||||
│ jobs/ — Nomad jobspecs: forgejo.hcl (Vault secrets via template, S2.4); woodpecker-server.hcl + woodpecker-agent.hcl (host-net, docker.sock, Vault KV, S3.1-S3.2); agents.hcl (7 roles, llama, Vault-templated bot tokens, S4.1); vault-runner.hcl (parameterized batch dispatch, S5.3); staging.hcl (Caddy file-server, dynamic port — edge discovers via service registration, S5.2); chat.hcl (Claude chat UI, tmpfs via mount block, Vault OAuth secrets, S5.2); edge.hcl (Caddy proxy + dispatcher sidecar, S5.1)
|
||||
├── projects/ *.toml.example — templates; *.toml — local per-box config (gitignored)
|
||||
├── formulas/ Issue templates (TOML specs for multi-step agent tasks)
|
||||
├── docker/ Dockerfiles and entrypoints: reproduce, triage, edge dispatcher, chat (server.py, entrypoint-chat.sh, Dockerfile, ui/)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# Architect — Agent Instructions
|
||||
|
||||
## What this agent is
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# Dev Agent
|
||||
|
||||
**Role**: Implement issues autonomously — write code, push branches, address
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
# disinto-chat — minimal HTTP backend for Claude chat UI
|
||||
#
|
||||
# Small Debian slim base with Python runtime.
|
||||
# Small Debian slim base with Python runtime and Node.js.
|
||||
# Chosen for simplicity and small image size (~100MB).
|
||||
#
|
||||
# Image size: ~100MB (well under the 200MB ceiling)
|
||||
#
|
||||
# The claude binary is mounted from the host at runtime via docker-compose,
|
||||
# not baked into the image — same pattern as the agents container.
|
||||
# Claude CLI is baked into the image — same pattern as the agents container.
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install Python (no build-time network access needed)
|
||||
# Install Node.js (required for Claude CLI) and Python
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3 \
|
||||
nodejs npm python3 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Claude Code CLI — chat backend runtime
|
||||
RUN npm install -g @anthropic-ai/claude-code@2.1.84
|
||||
|
||||
# Non-root user — fixed UID 10001 for sandbox hardening (#706)
|
||||
RUN useradd -m -u 10001 -s /bin/bash chat
|
||||
|
||||
|
|
|
|||
|
|
@ -234,6 +234,13 @@ fi
|
|||
rm -f "$_fetch_log"
|
||||
done) &
|
||||
|
||||
# Nomad template renders Caddyfile to /local/Caddyfile via service discovery;
|
||||
# copy it into the expected location if present (compose uses the mounted path).
|
||||
if [ -f /local/Caddyfile ]; then
|
||||
cp /local/Caddyfile /etc/caddy/Caddyfile
|
||||
echo "edge: using Nomad-rendered Caddyfile from /local/Caddyfile" >&2
|
||||
fi
|
||||
|
||||
# Caddy as main process — run in foreground via wait so background jobs survive
|
||||
# (exec replaces the shell, which can orphan backgrounded subshells)
|
||||
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile &
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# Gardener Agent
|
||||
|
||||
**Role**: Backlog grooming — detect duplicate issues, missing acceptance
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
{"issue":915,"group":"lib/generators.sh","title":"remove no-op sed in generate_compose --build mode","reason":"sed replaces agents: with itself — no behavior change; single-line removal","ts":"2026-04-17T01:04:05Z"}
|
||||
|
|
@ -1,12 +1 @@
|
|||
[
|
||||
{
|
||||
"action": "edit_body",
|
||||
"issue": 996,
|
||||
"body": "Flagged by AI reviewer in PR #993.\n\n## Problem\n\nThe consul-template with/else/end pattern using aggressive whitespace trimming (e.g. `{{- with secret ... -}}` / `{{- else -}}` / `{{- end }}` then immediately `{{- with`) strips all newlines between consecutive single-variable env blocks at parse time. This would render the secrets env file as one concatenated line (`GITHUB_TOKEN=valCODEBERG_TOKEN=val...`), which Nomad's `env = true` cannot parse correctly.\n\n## Why not blocked\n\nagents.hcl has been runtime-tested (S4-fix-6 and S4-fix-7 made observable runtime fixes). If the env file were broken, all bot tokens would be absent — a loud, observable failure. This suggests consul-template may handle whitespace trimming differently from raw Go text/template. Needs runtime verification.\n\n## Verification\n\nDeploy either job and inspect the rendered secrets file:\n```\nnomad alloc exec <alloc-id> cat /secrets/bots.env\n```\nConfirm each KEY=VALUE pair is on its own line.\n\n---\n*Auto-created from AI review*\n\n## Affected files\n- `nomad/jobs/agents.hcl` — bots.env template (lines 147-189)\n- `nomad/jobs/vault-runner.hcl` — runner.env template (PR #993)\n\n## Acceptance criteria\n- [ ] Deploy `agents` or `vault-runner` job on factory host\n- [ ] Inspect rendered secrets file: `nomad alloc exec <alloc-id> cat /secrets/bots.env`\n- [ ] Confirm each KEY=VALUE pair is on its own line (not concatenated)\n- [ ] If broken: fix whitespace trimming to preserve newlines between blocks; if fine, close as not-a-bug"
|
||||
},
|
||||
{
|
||||
"action": "add_label",
|
||||
"issue": 996,
|
||||
"label": "backlog"
|
||||
}
|
||||
]
|
||||
[]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# Shared Helpers (`lib/`)
|
||||
|
||||
All agents source `lib/env.sh` as their first action. Additional helpers are
|
||||
|
|
|
|||
|
|
@ -657,7 +657,6 @@ COMPOSEEOF
|
|||
|
||||
# In build mode, replace image: with build: for locally-built images
|
||||
if [ "$use_build" = true ]; then
|
||||
sed -i 's|^\( agents:\)|\1|' "$compose_file"
|
||||
sed -i '/^ image: ghcr\.io\/disinto\/agents:/{s|image: ghcr\.io/disinto/agents:.*|build:\n context: .\n dockerfile: docker/agents/Dockerfile\n pull_policy: build|}' "$compose_file"
|
||||
sed -i '/^ image: ghcr\.io\/disinto\/edge:/{s|image: ghcr\.io/disinto/edge:.*|build: ./docker/edge\n pull_policy: build|}' "$compose_file"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# nomad/ — Agent Instructions
|
||||
|
||||
Nomad + Vault HCL for the factory's single-node cluster. These files are
|
||||
|
|
@ -19,8 +19,8 @@ see issues #821–#992 for the step breakdown.
|
|||
| `jobs/woodpecker-server.hcl` | submitted via `lib/init/nomad/deploy.sh` | Woodpecker CI server; host networking, Vault KV for `WOODPECKER_AGENT_SECRET` + Forgejo OAuth creds (S3.1) |
|
||||
| `jobs/woodpecker-agent.hcl` | submitted via `lib/init/nomad/deploy.sh` | Woodpecker CI agent; host networking, `docker.sock` mount, Vault KV for `WOODPECKER_AGENT_SECRET`; `WOODPECKER_SERVER` uses `${attr.unique.network.ip-address}:9000` (Nomad interpolation) — port binds to LXC alloc IP, not localhost (S3.2, S3-fix-6, #964) |
|
||||
| `jobs/agents.hcl` | submitted via `lib/init/nomad/deploy.sh` | All 7 agent roles (dev, review, gardener, planner, predictor, supervisor, architect) + llama variant; Vault-templated bot tokens via `service-agents` policy; `force_pull = false` — image is built locally by `bin/disinto --with agents`, no registry (S4.1, S4-fix-2, S4-fix-5, #955, #972, #978) |
|
||||
| `jobs/staging.hcl` | submitted via `lib/init/nomad/deploy.sh` | Caddy file-server mounting `docker/` as `/srv/site:ro`; no Vault integration; internal-only via edge proxy (S5.2, #989) |
|
||||
| `jobs/chat.hcl` | submitted via `lib/init/nomad/deploy.sh` | Claude chat UI; custom `disinto/chat:local` image; sandbox hardening (cap_drop ALL, tmpfs, pids_limit 128); Vault-templated OAuth secrets via `service-chat` policy (S5.2, #989) |
|
||||
| `jobs/staging.hcl` | submitted via `lib/init/nomad/deploy.sh` | Caddy file-server mounting `docker/` as `/srv/site:ro`; no Vault integration; **dynamic host port** (no static 80 — edge owns 80/443, collision fixed in S5-fix-7 #1018); edge discovers via Nomad service registration (S5.2, #989) |
|
||||
| `jobs/chat.hcl` | submitted via `lib/init/nomad/deploy.sh` | Claude chat UI; custom `disinto/chat:local` image; sandbox hardening (cap_drop ALL, **tmpfs via mount block** not `tmpfs=` arg — S5-fix-5 #1012, pids_limit 128); Vault-templated OAuth secrets via `service-chat` policy (S5.2, #989) |
|
||||
| `jobs/edge.hcl` | submitted via `lib/init/nomad/deploy.sh` | Caddy reverse proxy + dispatcher sidecar; routes /forge, /woodpecker, /staging, /chat; uses `disinto/edge:local` image built by `bin/disinto --with edge`; Vault-templated ops-repo creds via `service-dispatcher` policy (S5.1, #988) |
|
||||
|
||||
Nomad auto-merges every `*.hcl` under `-config=/etc/nomad.d/`, so the
|
||||
|
|
|
|||
|
|
@ -89,18 +89,22 @@ job "chat" {
|
|||
config {
|
||||
image = "disinto/chat:local"
|
||||
force_pull = false
|
||||
# Sandbox hardening (#706): cap_drop ALL (no Linux capabilities)
|
||||
# tmpfs /tmp for runtime files (64MB)
|
||||
# pids_limit 128 (prevent fork bombs)
|
||||
# Sandbox hardening (#706): cap_drop ALL, pids_limit 128, tmpfs /tmp
|
||||
# ReadonlyRootfs enforced via entrypoint script (fails if running as root)
|
||||
cap_drop = ["ALL"]
|
||||
pids_limit = 128
|
||||
mount {
|
||||
type = "tmpfs"
|
||||
target = "/tmp"
|
||||
readonly = false
|
||||
tmpfs_options {
|
||||
size = 67108864 # 64MB in bytes
|
||||
}
|
||||
}
|
||||
# Security options for sandbox hardening
|
||||
# apparmor=unconfined needed for Claude CLI ptrace access
|
||||
# no-new-privileges prevents privilege escalation
|
||||
cap_drop = ["ALL"]
|
||||
pids_limit = 128
|
||||
security_opt = ["apparmor=unconfined", "no-new-privileges"]
|
||||
# tmpfs mount via volumes config (Nomad Docker driver)
|
||||
volumes = ["tmpfs:/tmp:size=64m"]
|
||||
}
|
||||
|
||||
# ── Volume mounts ──────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -114,6 +114,58 @@ job "edge" {
|
|||
read_only = false
|
||||
}
|
||||
|
||||
# ── Caddyfile via Nomad service discovery (S5-fix-7, issue #1018) ────
|
||||
# Renders staging upstream from Nomad service registration instead of
|
||||
# hardcoded staging:80. Caddy picks up /local/Caddyfile via entrypoint.
|
||||
template {
|
||||
destination = "local/Caddyfile"
|
||||
change_mode = "restart"
|
||||
data = <<EOT
|
||||
# Caddyfile — edge proxy configuration (Nomad-rendered)
|
||||
# Staging upstream discovered via Nomad service registration.
|
||||
|
||||
:80 {
|
||||
# Redirect root to Forgejo
|
||||
handle / {
|
||||
redir /forge/ 302
|
||||
}
|
||||
|
||||
# Reverse proxy to Forgejo
|
||||
handle /forge/* {
|
||||
reverse_proxy forgejo:3000
|
||||
}
|
||||
|
||||
# Reverse proxy to Woodpecker CI
|
||||
handle /ci/* {
|
||||
reverse_proxy woodpecker:8000
|
||||
}
|
||||
|
||||
# Reverse proxy to staging — dynamic port via Nomad service discovery
|
||||
handle /staging/* {
|
||||
{{ range nomadService "staging" }} reverse_proxy {{ .Address }}:{{ .Port }}
|
||||
{{ end }} }
|
||||
|
||||
# Chat service — reverse proxy to disinto-chat backend (#705)
|
||||
# OAuth routes bypass forward_auth — unauthenticated users need these (#709)
|
||||
handle /chat/login {
|
||||
reverse_proxy chat:8080
|
||||
}
|
||||
handle /chat/oauth/callback {
|
||||
reverse_proxy chat:8080
|
||||
}
|
||||
# Defense-in-depth: forward_auth stamps X-Forwarded-User from session (#709)
|
||||
handle /chat/* {
|
||||
forward_auth chat:8080 {
|
||||
uri /chat/auth/verify
|
||||
copy_headers X-Forwarded-User
|
||||
header_up X-Forward-Auth-Secret {$FORWARD_AUTH_SECRET}
|
||||
}
|
||||
reverse_proxy chat:8080
|
||||
}
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
# ── Non-secret env ───────────────────────────────────────────────────
|
||||
env {
|
||||
FORGE_URL = "http://forgejo:3000"
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
# served to staging environment users.
|
||||
#
|
||||
# Network:
|
||||
# No external port exposed — edge proxy routes to it internally.
|
||||
# Service discovery via Nomad native provider for internal routing.
|
||||
# Dynamic host port — edge discovers via Nomad service registration.
|
||||
# No static port to avoid collisions with edge (which owns 80/443).
|
||||
#
|
||||
# Not the runtime yet: docker-compose.yml is still the factory's live stack
|
||||
# until cutover. This file exists so CI can validate it and S5.2 can wire
|
||||
|
|
@ -27,11 +27,10 @@ job "staging" {
|
|||
|
||||
# No Vault integration needed — no secrets required (static file server)
|
||||
|
||||
# Internal service — no external port. Edge proxy routes internally.
|
||||
# Internal service — dynamic host port. Edge discovers via Nomad service.
|
||||
network {
|
||||
port "http" {
|
||||
static = 80
|
||||
to = 80
|
||||
to = 80
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# Planner Agent
|
||||
|
||||
**Role**: Strategic planning using a Prerequisite Tree (Theory of Constraints),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# Predictor Agent
|
||||
|
||||
**Role**: Abstract adversary (the "goblin"). Runs a 2-step formula
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# Review Agent
|
||||
|
||||
**Role**: AI-powered PR review — post structured findings and formal
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# Supervisor Agent
|
||||
|
||||
**Role**: Health monitoring and auto-remediation, executed as a formula-driven
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<!-- last-reviewed: 8fc3ba5b59cd6cb15bd01ca0658cfea2bcb12068 -->
|
||||
<!-- last-reviewed: 3fb2de4a8ab500707665adfbf954aa1921ae7775 -->
|
||||
# vault/policies/ — Agent Instructions
|
||||
|
||||
HashiCorp Vault ACL policies for the disinto factory. One `.hcl` file per
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue