{"id":50913514,"url":"https://github.com/leebaroneau/template-agent","last_synced_at":"2026-06-16T12:32:24.103Z","repository":{"id":358405632,"uuid":"1240396628","full_name":"leebaroneau/template-agent","owner":"leebaroneau","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-11T02:13:26.000Z","size":755,"stargazers_count":0,"open_issues_count":26,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T02:16:56.711Z","etag":null,"topics":["agent-orchestration","ai-agents","gbrain","hermes","mcp","paperclip"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leebaroneau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-16T04:56:19.000Z","updated_at":"2026-06-10T22:50:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/leebaroneau/template-agent","commit_stats":null,"previous_names":["leebaroneau/paperclip-hermes-gbrain","leebaroneau/template-agent"],"tags_count":0,"template":true,"template_full_name":null,"purl":"pkg:github/leebaroneau/template-agent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leebaroneau%2Ftemplate-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leebaroneau%2Ftemplate-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leebaroneau%2Ftemplate-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leebaroneau%2Ftemplate-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leebaroneau","download_url":"https://codeload.github.com/leebaroneau/template-agent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leebaroneau%2Ftemplate-agent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34406820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-orchestration","ai-agents","gbrain","hermes","mcp","paperclip"],"created_at":"2026-06-16T12:32:23.196Z","updated_at":"2026-06-16T12:32:24.083Z","avatar_url":"https://github.com/leebaroneau.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Template Agent\n\nBlank Coolify-ready template for running Paperclip with Hermes Agent.\n\nThis repo is intentionally client-neutral. It should contain the deploy recipe only. Paperclip projects, Hermes profiles, API keys, and client data are created at runtime inside the Coolify volume mounted at `/data`.\n\n## ⚠️ For Agents (Claude, Codex, any LLM editing this repo) — Read First\n\nThis is a **template deployed to multiple companies simultaneously** from source. Several\nCoolify instances (one per brand) watch this repo's `main` branch and each run their own\n`agent-\u003cbrand\u003e` app, so a push to `main` can redeploy all of them at once. Brand-specific\nCoolify hosts, app UUIDs, and domains live in each brand's own deployment — never in this repo.\n\nProduction Coolify deploys should pull a prebuilt image from GitHub Container Registry. GitHub Actions builds and audits the image first, then publishes immutable tags such as `ghcr.io/leebaroneau/template-agent:sha-\u003ccommit\u003e`. Local development still builds from source by adding `compose.build.yaml`.\n\n**Rules for any change you propose:**\n\n- A push to a watched branch can redeploy **every Coolify watching that branch — simultaneously**. Treat every commit as a multi-tenant change.\n- Per-company customization lives in **Coolify env vars only** (`PAPERCLIP_HOSTNAME`, `PAPERCLIP_API_KEY`, `PAPERCLIP_DEFAULT_COMPANY_ID`, `HERMES_PROFILES`, `PROFILE_SYNC_ENABLED`, `HERMES_DASHBOARD_ENABLED`, …) — **never** introduce per-brand branches or hard-coded brand specifics in `compose.yaml`.\n- Hermes dashboard is **off by default**. Do not expose a Hermes service domain unless `HERMES_DASHBOARD_ENABLED=1` is intentional for that deployment. Use Paperclip as the primary UI, and use Hermes CLI/MCP/gateways behind it.\n- Data volumes are per-Coolify-app (`\u003capp_uuid\u003e_paperclip-data`). Rebuilds preserve data; only `docker volume rm` destroys it.\n- When asked \"add feature X for one company,\" gate it behind an env var; do **not** fork or branch the compose.\n\nIf you would be tempted to add a feature, env var, or compose section that only one brand needs — **stop and ask the user first.** The unified-branch architecture is deliberate.\n\n## Shape\n\n```text\npaperclip.\u003cclient-domain\u003e -\u003e paperclip:3100\nHermes dashboard is unrouted by default.\n\nPaperclip company → CEO agent → delegates to subordinate agents\n       │\n       ▼ each Paperclip agent uses adapterType: hermes_local\n       │\n       ▼\nhermes CLI runs locally inside the paperclip container\n       │\n       ▼\n       HERMES_HOME=/data/hermes/profiles/\u003ccompany-role\u003e   (per-agent profile)\n```\n\nOne image runs two services. Paperclip orchestrates and is the only default public UI. The Hermes service stays headless by default so profile bootstrap and gateway autostart can run without exposing a second browser UI. Both services share `/data`, so skills and the org chart are visible from either side.\n\nThe Paperclip MCP server (see below) closes the loop: Hermes-side agents can file and update Paperclip issues without leaving the conversation.\n\n## Services\n\n- `paperclip` runs Paperclip on port `3100`.\n- `hermes` bootstraps Hermes profiles and starts configured gateways. It only runs the dashboard on port `9119` when `HERMES_DASHBOARD_ENABLED=1`.\n- Both services share the `paperclip-data` volume at `/data`.\n\n## Image Tags and Deployment\n\n`compose.yaml` pulls the published image (`${TEMPLATE_AGENT_IMAGE:-ghcr.io/leebaroneau/template-agent:latest}`). `compose.build.yaml` is the local-build overlay that builds the stack from this repo:\n\n```bash\ndocker compose -f compose.yaml -f compose.build.yaml --env-file .env.example build\n```\n\nOn push to `main` (or manual `workflow_dispatch`), `.github/workflows/build-image.yml` builds `paperclip/Dockerfile`, audits it for client-neutrality, then tags and pushes to `ghcr.io/leebaroneau/template-agent`:\n\n| Event | Tags |\n|---|---|\n| Push or manual dispatch on `main` | `latest` + `sha-\u003ccommit\u003e` |\n| Manual dispatch on a non-`main` branch | `sha-\u003ccommit\u003e` |\n| Pull request | No image is published |\n\nAfter publishing, the workflow fires each configured brand's Coolify deploy webhook (gated on `main` + that brand's `COOLIFY_*_APP_UUID` repo var). Consuming `agent-\u003cbrand\u003e` repos either pin `TEMPLATE_AGENT_IMAGE` to a `sha-\u003ccommit\u003e` (manual roll-forward, deterministic rollback) or track `:latest` (auto-advance on each build). See `DEPLOYMENT.md` for the full model.\n\n## Paperclip Version\n\nThis draft template branch builds Paperclip from `PAPERCLIP_GIT_REPO` / `PAPERCLIP_GIT_REF`, currently `paperclipai/paperclip` PR #6243. That stacked PR includes the runtime identity and tool access API work from PRs #6230, #6242, and #6243.\n\nDo not clear `PAPERCLIP_GIT_REF` on this branch until those Paperclip changes have shipped in the published `paperclipai` package; the removed Hermes defaults patch is now owned by PR #6230. To return this template to the normal release path after publish, build with `PAPERCLIP_GIT_REF=` and bump the `PAPERCLIP_VERSION` Docker build arg. `profile-sync.mjs` now adopts Paperclip's `metadata.runtimeIdentity.profileSlug` and `metadata.runtimeIdentity.hermesHome` when present, then falls back to its legacy `/data/hermes/profiles/\u003ccompany-role\u003e` layout for older Paperclip builds.\n\nWhen `PAPERCLIP_GIT_REF` is set, the image keeps the checked-out Paperclip workspace at `/opt/paperclip-src` and runs the CLI through `tsx`. This is intentional for the draft branch: packing only `./cli` would pull the published `@paperclipai/server` package from npm and would not actually test the PR's server, DB, shared, UI, and adapter-utils changes.\n\nThe tool access seed below is safe to run before those Paperclip APIs exist: on older Paperclip builds it receives a 404, logs a skip, and leaves the stack unchanged.\n\n## /data Volume Layout\n\nEverything persistent lives under `/data`, mounted from the `paperclip-data` Docker volume.\n\n| Path | What lives there |\n|---|---|\n| `/data/paperclip.db` | Paperclip's SQLite database (companies, agents, issues, approvals, runs) |\n| `/data/instances/\u003ccompany-slug\u003e/` | Per-company project files, plan documents, attachments |\n| `/data/instances/\u003ccompany-slug\u003e/runtimes/hermes/profiles/\u003cruntime-id\u003e/` | Paperclip-owned Hermes runtime identity profile homes, adopted by profile-sync when present |\n| `/data/hermes/` | Default Hermes profile (config, skills, kanban, memory) |\n| `/data/hermes/profiles/\u003ccompany-role\u003e/` | Per-agent isolated Hermes profile (auto-created by profile-sync) |\n| `/data/hermes/archive/` | Archived profiles for terminated agents (`PROFILE_SYNC_DELETE_MODE=archive`) |\n| `/data/agent-stack/important-information-index.md` | Shared human-maintained index (see below) |\n| `/data/agent-stack/learning-protocol.md` | Shared learning protocol (see below) |\n| `/data/agent-stack/delegation-protocol.md` | Shared delegation contract (see below) |\n| `/data/agent-stack/org-chart.{md,json}` | Mirrored Paperclip org chart (when profile-sync enabled) |\n| `/data/agent-stack/profile-sync/manifest.json` | Profile-sync state (which agents got which slugs) |\n| `/data/.paperclip/` | Paperclip CLI auth state (board user credentials) |\n\nThe Dockerfile clears `/data` at build time. Anything you see there at runtime was created after the container started.\n\n## Local Smoke Test\n\n```bash\ncp .env.example .env\n./scripts/local-up.sh\n```\n\nThen open:\n\n- Paperclip: `http://localhost:3100`\n- Hermes dashboard: disabled unless `HERMES_DASHBOARD_ENABLED=1`\n\nStop it with:\n\n```bash\n./scripts/local-down.sh\n```\n\n## First-Run Flow\n\nAfter the stack is deployed (locally or via Coolify) and the containers are running:\n\n1. **Open Paperclip and claim the first admin user.** Paperclip runs in `PAPERCLIP_DEPLOYMENT_MODE=authenticated`. The first visitor to `https://paperclip.\u003cyour-domain\u003e/` (or `http://localhost:3100/` locally) is prompted to claim the instance.\n\n   \u003e ⚠️ **Save the email + password you set on this screen into a password manager *before* you click submit.** There is no email-based password reset in this stack (no SMTP is configured by default). If you lose those credentials, see \"Lost admin access?\" below.\n\n   If no claim flow appears at all (e.g. an admin was already created but the invite link expired before being used), open a shell in the container and mint a fresh one-time invite URL:\n\n   ```bash\n   # From the Coolify host, or via `coolify exec`:\n   docker exec -u node -it \u003cpaperclip-container\u003e \\\n     paperclipai auth bootstrap-ceo \\\n     --data-dir /data \\\n     --base-url https://paperclip.\u003cyour-domain\u003e \\\n     --force\n   ```\n\n   The CLI prints `Invite URL: https://paperclip.\u003cyour-domain\u003e/invite/\u003ctoken\u003e` (expires in 72 hours). Open it in a browser to create the admin.\n\n   **Lost admin access?** Same command. `--force` revokes any previous bootstrap invite and issues a new one even if an `instance_admin` already exists. The original admin row stays in the DB but you can sign in via the new invite and demote/remove it.\n\n2. **Create or claim a company.** Each company is its own Paperclip workspace. Set a goal, name a CEO. The default Hermes agent is seeded automatically when profile-sync is enabled — otherwise see \"Seed Default Hermes Agent\" below.\n\n3. **Mint a board API key.**\n\n   \u003e ⚠️ **There is no \"API Keys\" page in the Paperclip dashboard.** The only way to create a `pcp_board_*` token is via the CLI flow below. You'll need this token for `PAPERCLIP_API_KEY` (and `PAPERCLIP_PROFILE_SYNC_API_KEY` if you enable profile sync).\n\n   **Option A — From inside the running container** (Coolify Terminal, `docker exec`, or your local compose):\n\n   ```bash\n   docker compose --env-file .env exec paperclip paperclipai auth login --api-base http://127.0.0.1:3100\n   ```\n\n   **Option B — Drive the API directly from anywhere** (no container shell needed). The CLI just wraps two HTTP calls:\n\n   ```bash\n   # 1. Create a challenge — note the boardApiToken and approvalUrl in the response.\n   curl -s -X POST https://paperclip.\u003cyour-domain\u003e/api/cli-auth/challenges \\\n     -H \"Content-Type: application/json\" \\\n     -d '{\"command\":\"manual\",\"clientName\":\"manual\",\"requestedAccess\":\"board\",\"requestedCompanyId\":null}'\n\n   # 2. Open `approvalUrl` in your browser, sign in as the admin from step 1, click Approve.\n\n   # 3. Poll until the challenge flips to status=\"approved\":\n   curl -s \"https://paperclip.\u003cyour-domain\u003e/api/cli-auth/challenges/\u003cid\u003e?token=\u003cchallenge-token\u003e\"\n   ```\n\n   Once approved, the `boardApiToken` from step 1 is your live `pcp_board_*` key. **Copy it now into a password manager — the API does not let you retrieve it again later.** Mint additional keys anytime by repeating either flow.\n\n4. **Activate the key — write it to the shared volume** (preferred) or set it as a Coolify env var.\n\n   **Preferred — write to the shared volume** so the key persists across image rebuilds and is picked up automatically by both services on every restart:\n\n   ```bash\n   # From inside the paperclip container:\n   KEY=\"pcp_board_\u003cyour-token\u003e\"\n   mkdir -p /data/agent-stack/profile-sync\n   # Upsert both keys (remove stale blank lines first)\n   sed -i '/^PAPERCLIP_API_KEY=/d; /^PAPERCLIP_PROFILE_SYNC_API_KEY=/d' \\\n     /data/agent-stack/profile-sync/profile-sync.env 2\u003e/dev/null || true\n   printf 'PAPERCLIP_API_KEY=%s\\nPAPERCLIP_PROFILE_SYNC_API_KEY=%s\\n' \"$KEY\" \"$KEY\" \\\n     \u003e\u003e /data/agent-stack/profile-sync/profile-sync.env\n   ```\n\n   The `paperclip` service sources `/data/agent-stack/profile-sync/profile-sync.env` at startup. The `hermes` service does too (as of this commit) — so both services pick up the key automatically on every container restart without any Coolify env var update.\n\n   **Alternative — set in Coolify env vars** (also works but requires a redeploy each time the key rotates):\n\n   ```\n   PAPERCLIP_API_KEY=\u003cpcp_board_...\u003e\n   PAPERCLIP_PROFILE_SYNC_API_KEY=\u003csame-key\u003e\n   ```\n\n5. **Restart the `hermes` container** (not a full redeploy) to pick up the key from the volume:\n\n   ```bash\n   # Coolify UI: restart the hermes service only, or:\n   docker compose --env-file .env restart hermes\n   ```\n\n   If you used the Coolify env var approach instead, trigger a full redeploy so the new env is injected.\n\n7. **Use Paperclip as the main interface**, or talk to Hermes through any configured messaging gateway. If you intentionally enable the dashboard with `HERMES_DASHBOARD_ENABLED=1`, you can also use the Hermes dashboard route. Hermes can call Paperclip tools — say *\"list paperclip companies\"* and the MCP server replies with the live roster.\n\n## Coolify Setup\n\n1. **Create a new Docker Compose app** in Coolify pointing at this GitHub repo (`leebaroneau/template-agent`, branch `main`, base directory `/`).\n2. **Wire up a GitHub source that can read this repo** (skip if the repo is public). Coolify's \"Public GitHub\" source can only clone public repos. For a private template, attach the app to a GitHub App installation that includes this repo:\n   - In Coolify: app → *Source* → pick (or create) a GitHub App installation, and ensure the installation is granted access to this repo on GitHub.\n   - Symptom of missing this step: deploy fails in ~0 seconds with `GitHub API call failed: Not Found` in the logs.\n3. **Pick the public Paperclip domain** you'll use:\n   - `paperclip.\u003cclient-domain\u003e`\n4. **Generate starter env values**:\n\n   ```bash\n   ./scripts/coolify-env.sh client.example.com\n   ```\n\n5. **Paste the generated values into Coolify**, replacing the example domain with the real client domain.\n6. **Set the Paperclip service domain in the Coolify app UI.** Open the app → *Configuration* → set a domain for `paperclip` (`paperclip.\u003cclient-domain\u003e`). Coolify uses this map to inject the working Traefik router — the compose's own `${PAPERCLIP_HOSTNAME}` placeholder is not substituted under Coolify (see \"Coolify routing notes\" below).\n\n   Do **not** set a `hermes` service domain unless you also set `HERMES_DASHBOARD_ENABLED=1` and intentionally want the Hermes browser UI exposed for debugging/admin use.\n\n7. **(Optional) Render brand-specific compose routes** if you'd rather hardcode the Traefik labels in a brand fork:\n\n   ```bash\n   ./scripts/render-coolify-compose.sh client.example.com client-agent-stack\n   ```\n\n8. **Deploy.** Coolify pulls the image referenced by `TEMPLATE_AGENT_IMAGE` and starts `paperclip` + `hermes` from that already-audited artifact. Then follow the First-Run Flow above to mint the API key and activate the MCP server.\n\n### Auto-deploy from `main`\n\nUse Coolify's Git integration or deploy webhook for deploys, but keep the deployment image-first:\n\n```env\nTEMPLATE_AGENT_IMAGE=ghcr.io/leebaroneau/template-agent:sha-$SOURCE_COMMIT\n```\n\nLeave Coolify's **Literal** toggle off for this variable so `$SOURCE_COMMIT` expands to the commit being deployed. GitHub Actions publishes `sha-\u003ccommit\u003e` on every push to `main`. Coolify should not build the Paperclip/Hermes image on the production host.\n\nDo not let Coolify auto-deploy a pushed commit before the GitHub image workflow has finished. The safe sequence is:\n\n1. Merge to `main`.\n2. Wait for **Build \u0026 push agent stack image** to publish `ghcr.io/leebaroneau/template-agent:sha-\u003cmerge-commit\u003e`.\n3. Trigger the Coolify deploy for that commit.\n4. Confirm `https://paperclip.\u003cclient-domain\u003e/api/health` returns `{\"status\":\"ok\"}` before removing any recovery container or rollback tag.\n\nIf you want full automation later, wire Coolify's deploy webhook from the image workflow after the push step, not directly from GitHub's branch push event.\n\n### Recovering from a stuck deploy\n\nIf Coolify skips a deploy or keeps running an older built image, trigger a force redeploy from Coolify for the affected app. Telltale signs:\n\n- `docker inspect \u003ccontainer\u003e --format '{{index .Config.Labels \"org.opencontainers.image.revision\"}}'` shows an old commit SHA.\n- New env vars from a fresh PR are missing inside the container.\n- A line you just added to a baked-in file (e.g. `paperclip/profile-sync.mjs`) is not present at `/opt/paperclip/profile-sync.mjs`.\n\nBecause production deploys pull prebuilt images, recovery should be a rollback to the previous known-good `ghcr.io/leebaroneau/template-agent:sha-\u003ccommit\u003e` tag followed by a Coolify redeploy. Do not force a production-host image rebuild as the first recovery step.\n\nTo trigger a force redeploy across all Coolify deployments, use the `force` dispatch input:\n\n```bash\ngh workflow run build-image.yml --ref main -f force=true\n```\n\n### Coolify env variable checklist\n\n**Required for any deployment:**\n\n```env\nPAPERCLIP_PUBLIC_URL=https://paperclip.\u003cclient-domain\u003e\nPAPERCLIP_ALLOWED_HOSTNAMES=paperclip.\u003cclient-domain\u003e,localhost,127.0.0.1\nPAPERCLIP_HOSTNAME=paperclip.\u003cclient-domain\u003e\nHERMES_DASHBOARD_ENABLED=0\n```\n\nPublic routing is configured separately via the Coolify per-service domain map (step 6 of *Setting Up A New Coolify Stack*) — not via env vars. `PAPERCLIP_HOSTNAME` is only used by the compose's own Traefik labels for plain `docker compose` deployments, which Coolify doesn't substitute (see \"Coolify routing notes\"). Keeping it set on a Coolify deploy is harmless and documents intent.\n\n**Required to activate the Paperclip MCP server** (set after First-Run step 3 mints a key):\n\n```env\nPAPERCLIP_API_KEY=\u003cpcp_board_...\u003e\nPAPERCLIP_DEFAULT_COMPANY_ID=\u003cuuid\u003e   # optional, single-company convenience\n```\n\n**Profile sync** — starts automatically when `PAPERCLIP_PROFILE_SYNC_API_KEY` is set. Generates the org chart and gives each Paperclip agent its own isolated Hermes profile. Set `PROFILE_SYNC_ENABLED=0` to explicitly disable (e.g. local dev without a full Paperclip setup).\n\n```env\nPROFILE_SYNC_INTERVAL_SEC=60\nPROFILE_SYNC_DELETE_MODE=archive\nPROFILE_SYNC_GRANT_MANAGER_ASSIGN_TASKS=1\nPROFILE_SYNC_HERMES_MODEL_MODE=inherit\nPROFILE_SYNC_DEFAULT_COMPANY_SKILLS=use-100m-framework\nPAPERCLIP_PROFILE_SYNC_API_KEY=\u003cpcp_board_...\u003e   # same key as PAPERCLIP_API_KEY is fine\nTOOL_ACCESS_SEED_ENABLED=1\nTOOL_ACCESS_APPLY_DEFAULT_PRESET=1\nTOOL_ACCESS_DEFAULT_PRESET=agent-stack-hermes-default\n```\n\nProfile sync also grants `canAssignTasks` to active agents that have direct reports, preserving their existing `canCreateAgents` setting. Disable with `PROFILE_SYNC_GRANT_MANAGER_ASSIGN_TASKS=0` if a deployment wants CEO-only task assignment.\n\nBy default, profile sync writes each managed `hermes_local` agent's `adapterConfig.model` / `provider` from that role's Hermes profile config (`PROFILE_SYNC_HERMES_MODEL_MODE=inherit`). Set `PROFILE_SYNC_HERMES_MODEL_MODE=paperclip-default` when you want the Paperclip UI to show `Model default` for all managed Hermes agents and let the Hermes profile choose the model at execution time. In that mode, profile sync explicitly clears stale `model` and `provider` values on every managed Hermes agent.\n\nProfile sync also keeps baseline runtime skills in Paperclip's company skill\nlist. `PROFILE_SYNC_DEFAULT_COMPANY_SKILLS` defaults to\n`use-100m-framework`; for each listed slug, profile-sync reads\n`/opt/hermes-runtime/skills/\u003cslug\u003e/SKILL.md`, creates the company skill if it is\nmissing, then uses the Paperclip company skill list as the desired skill set for\nmanaged Hermes roles. Existing company skills are never patched or replaced, so\ncompany-specific skill edits stay intact. Set\n`PROFILE_SYNC_DEFAULT_COMPANY_SKILLS=none` to disable this seeding.\n\nGrants are **tracked in `/data/agent-stack/profile-sync/manifest.json` under `permissionedAgents`** and **revoked on a future cycle if the agent loses qualification** (e.g. its last direct report leaves). CEOs (`agent.role === 'ceo'`) are skipped in both grant and revoke paths because Paperclip surfaces their `canAssignTasks` via the role-derived `taskAssignSource: ceo_role` permission — the explicit-grant lifecycle is for non-CEO managers. Agents granted before this manifest tracking shipped are *not* eligible for the steady-state revoke; see [Cleaning up historical canAssignTasks drift](#cleaning-up-historical-canassigntasks-drift) below for the one-shot cleanup tool.\n\n**Gateway autostart for profiles with messaging credentials:**\n\n```env\nHERMES_GATEWAY_AUTOSTART=1\nHERMES_GATEWAY_PROFILES=auto\n```\n\n`auto` starts any existing Hermes profile whose `.env` contains a messaging credential such as `TELEGRAM_BOT_TOKEN`. To pin an explicit set, use a comma-separated list like `sales,support`. To disable gateway autostart, set `HERMES_GATEWAY_AUTOSTART=0`.\n\n**Do NOT add blank LLM provider keys** (`OPENAI_API_KEY=`, `ANTHROPIC_API_KEY=`, `OPENROUTER_API_KEY=`) to Coolify. Hermes boots without them. Configure providers through Hermes CLI/config or temporarily enable the dashboard for admin setup, then turn it back off.\n\nFor single-VM deployments, profile-sync env can live in `/data/agent-stack/profile-sync/profile-sync.env` (root-readable) instead of Coolify env. Override `ORG_MIRROR_ROOT` only if you need the org chart files somewhere other than `/data/agent-stack`.\n\n### Do not override `PAPERCLIP_ALLOWED_HOSTNAMES` in Coolify\n\nThe compose builds this value automatically:\n\n```\npaperclip,localhost,127.0.0.1,\u003cPAPERCLIP_HOSTNAME\u003e\n```\n\nThe first three entries are the Docker-internal names the `hermes` service uses to call Paperclip (via `http://paperclip:3100`). `PAPERCLIP_HOSTNAME` appends the public domain so browser-originated requests are also accepted.\n\n**If you set `PAPERCLIP_ALLOWED_HOSTNAMES` as a Coolify env var it will override this value entirely**, stripping the internal names and causing every Hermes→Paperclip API call to return `403 Hostname '...' is not allowed`. Leave this variable unset in Coolify — the compose handles it correctly.\n\n### Coolify routing notes\n\nCoolify renders `docker-compose.yaml` with `$` escaped to `$$` inside the `labels:` block. That means `${PAPERCLIP_HOSTNAME}` in the Traefik labels stays *literal* instead of being substituted — and the same is true for Coolify's own magic vars like `${SERVICE_FQDN_*}` when written into compose labels. Setting `SERVICE_FQDN_HERMES_9119` or `SERVICE_FQDN_HERMES` as an env var does NOT generate routing labels on its own.\n\nWhat Coolify *does* read is the per-service domain map on the application resource. Set it via the UI (recommended) or the API:\n\n1. **Coolify UI:** App → *Configuration* → set domain per service (`paperclip` → `paperclip.\u003cyour-domain\u003e`). Coolify auto-injects a working Traefik router (`http-0-\u003cuuid\u003e-paperclip.rule=Host(\\`...\\`)`) at next deploy. Add `hermes` only when the dashboard is intentionally enabled.\n2. **Coolify API:** `PATCH /api/v1/applications/\u003cuuid\u003e` with body `{\"docker_compose_domains\":{\"paperclip\":{\"name\":\"paperclip\",\"domain\":\"http://paperclip.\u003cyour-domain\u003e\"}}}`. Trigger a redeploy after — the change takes effect when the next deploy renders Traefik labels.\n\nSymptom of missing this step: `paperclip.\u003cyour-domain\u003e` returns 404 or routes to the wrong service. `docker inspect \u003cpaperclip-container\u003e | grep traefik` shows either no routers or routers with literal `${VAR}` text — both mean the per-service map was never set.\n\n### Traefik labels must be UUID-agnostic\n\n**Never write a Coolify resource UUID into a Traefik label in this compose.** Coolify auto-generates `routers.http-0-\u003cthis-app-uuid\u003e-\u003cservice\u003e` and `routers.https-0-\u003cthis-app-uuid\u003e-\u003cservice\u003e` per app at deploy time. The auto-gen path is the only correct one because each Coolify instance fills in its own UUID.\n\nEmbedding a literal UUID pins that router to one specific Coolify app. When the same compose is deployed to a second Coolify instance (different UUID), the second instance ends up with both its own correct auto-generated routers and the original instance's leaked router, all listening on the same `Host()`. Traefik then picks between them by priority/specificity, producing inconsistent behavior across requests.\n\nDo not add a shared hard-coded Hermes basic-auth hash to this template. If a deployment exposes the Hermes dashboard, protect it with deployment-level access control such as the existing auth-gate or Cloudflare Access.\n\nIf you find a `routers.https-0-\u003csome-uuid\u003e-...` or `routers.http-0-\u003csome-uuid\u003e-...` line written into this compose (by hand-edit, a previous merge, or a paste from a Coolify-rendered compose), delete it before committing — Coolify will regenerate the right version per deploy.\n\n## Paperclip MCP Server\n\nThe blank Hermes config is intentionally empty, with one exception: a Paperclip MCP server is wired in by default so Hermes agents in any new setup can file and track work in Paperclip through typed tool calls instead of constructing shell `curl` commands. Seeded agents and profile-sync-managed agents also get the Hermes `mcp` toolset in their Paperclip adapter config by default, so the profile config and the runtime tool access stay aligned.\n\nThe server lives at `paperclip/mcp-paperclip/` and is baked into the image at `/opt/paperclip/mcp-paperclip/`. It is registered in `hermes-runtime/templates/config.yaml` under `mcp_servers.paperclip`, exposing eight tools to every Hermes profile as the blank-template fallback:\n\n```text\npaperclip_list_companies\npaperclip_create_issue\npaperclip_list_issues\npaperclip_get_issue\npaperclip_update_issue\npaperclip_comment_on_issue\npaperclip_list_agents\npaperclip_list_projects\n```\n\nIssues created this way show up in Paperclip's task board exactly like any other.\n\nThe server reads its credentials from container env in this order:\n\n```text\nPAPERCLIP_API_KEY              (preferred)\nPAPERCLIP_PROFILE_SYNC_API_KEY (fallback)\n```\n\nIf both are blank the server still starts but every tool call fails with an auth error. Mint a board key once Paperclip is reachable (see \"Mint a board API key\" in First-Run Flow, step 3), then write it to the shared volume as described in step 4 — the hermes container picks it up on the next restart without a Coolify env var update or full redeploy.\n\nOptional convenience env: set `PAPERCLIP_DEFAULT_COMPANY_ID=\u003cuuid\u003e` so single-company setups don't need to pass `companyId` on every tool call.\n\nOn Paperclip builds with company tool access APIs, profile-sync seeds matching Paperclip MCP tool records and the selected preset renders per-agent `mcp_servers.paperclip.tools.include` lists into each `hermes_local` agent's adapter config.\n\nHealth check from inside the container:\n\n```bash\ndocker compose --env-file .env exec paperclip node /opt/paperclip/mcp-paperclip/server.mjs \\\n  \u003c\u003c\u003c '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"smoke\",\"version\":\"0\"}}}'\n```\n\nA healthy server replies with `serverInfo: {\"name\":\"paperclip\",\"version\":\"0.1.0\"}`.\n\n### Propagation to existing profiles\n\nWhen you (or an upstream update) add a new MCP server to `hermes-runtime/templates/config.yaml`, the `bootstrap-profiles.sh` entrypoint script idempotently merges any *missing* `mcp_servers.*` entries into every profile config on the next container start — both `HERMES_PROFILES`-listed profiles and per-role profiles that `profile-sync.mjs` creates from Paperclip runtime identity metadata under `/data/instances/default/runtimes/hermes/profiles/` (falling back to `/data/hermes/profiles/` for older Paperclip builds). Existing entries are never overwritten, so per-profile customisations are preserved. New servers added to the template propagate to every Hermes profile automatically without a manual patch.\n\n**Brand overlays.** Brand wrappers (e.g. an `agent-\u003cbrand\u003e` deploy repo) can contribute additional `mcp_servers` entries without modifying or forking this image. Drop YAML files into `/opt/hermes-runtime/templates/overlays/*.yaml` — typically via Docker Compose `configs:` mounts on both the `paperclip` and `hermes` services — and `bootstrap-profiles.sh` merges each file's `mcp_servers.*` into the effective template before merging that into each profile. The merge is strictly additive at both layers: the canonical `config.yaml` wins over any overlay on key collision, and existing profile entries always win over the effective template. Among overlays, alphabetic-first filename wins on collision.\n\nOverlay errors (malformed YAML, missing `mcp_servers` key, non-dict `mcp_servers` value) emit a single stderr warning and skip that overlay — bootstrap never crashes because of overlay errors. See `hermes-runtime/templates/overlays/README.md` (shipped in the image) for the contract a brand overlay file must follow.\n\n### Bundled skill: `using-paperclip`\n\nThe MCP server provides typed tools. The bundled `using-paperclip` Hermes skill provides *behaviour* — when and how agents should reach for those tools. It lives at `hermes-runtime/skills/using-paperclip/SKILL.md` and is symlinked into every Hermes profile's `skills/agent-stack/` directory by `bootstrap-profiles.sh`.\n\nThe skill teaches agents to:\n\n- Check their assigned issues at task start via `paperclip_list_issues`\n- Post status comments at meaningful milestones via `paperclip_comment_on_issue`\n- File subtasks as child issues rather than burying them in a comment\n- Use `@AgentName` mentions in comments to wake the right peer on blockers\n- Mark issues `done` with a summary at finish\n\nWithout this skill, the MCP tools still work — but agents only call them when a user explicitly asks. With the skill, agents treat Paperclip issue tracking as a first-class part of their loop.\n\nTo add your own agent-stack-wide skills, drop a `SKILL.md` (with optional `references/`, `scripts/`, `assets/`) under `hermes-runtime/skills/\u003cyour-skill-name\u003e/` and rebuild. The bootstrap sweep picks them up across every profile.\n\n## Runtime Patches\n\nThe `paperclip` container's entrypoint runs three small Node patches against the installed Paperclip runtime before starting the server. These rewrite a few lines in place each boot so the agent stack behaves correctly; the patch scripts resolve both the published npm layout and the `/opt/paperclip-src` PR-source layout used on this draft branch.\n\n| Patch | What it changes |\n|---|---|\n| `patch-hermes-adapter-env.mjs` | Unwrap Paperclip's env-binding objects when passing to the Hermes child process. Without this, `HERMES_HOME` and `PAPERCLIP_API_URL` reach Hermes as objects instead of strings. |\n| `patch-hermes-adapter-skills-home.mjs` | Rewrite `hermes-paperclip-adapter`'s `listSkills` so it scans `\u003cHERMES_HOME\u003e/skills/` (instead of always `$HOME/.hermes/skills/`) and follows symlinks at both the category and item levels. Without this, every per-role profile that profile-sync creates reports 0 skills in Paperclip's UI/API even though Hermes itself loads them fine. |\n| `patch-paperclip-company-prefix.mjs` | Relax Paperclip's company URL-key prefix constraints to allow the slugs the agent stack uses. |\n\nPaperclip PR #6230 owns Hermes runtime identity and model defaults, so this template no longer ships `patch-paperclip-hermes-defaults.mjs`.\n\nThe remaining three patches are idempotent and re-applied on every container start. The tool access seed does not replace these patches; keep them until the matching behavior has shipped upstream and the patch tests prove they are no longer needed. If you upgrade Paperclip (`PAPERCLIP_VERSION` build arg) or change `PAPERCLIP_GIT_REF`, re-run the patch tests:\n\n```bash\nnode paperclip/patch-hermes-adapter-env.test.mjs\nnode paperclip/patch-hermes-adapter-skills-home.test.mjs\nnode paperclip/patch-paperclip-company-prefix.test.mjs\n```\n\n## Seed Default Hermes Agent\n\n`paperclip/seed-agents.mjs` ensures every Paperclip company has a default `Hermes` agent ready to receive work, using `adapterType: hermes_local`. Profile-sync invokes it automatically; you can also run it by hand:\n\n```bash\nPAPERCLIP_API_BASE=http://localhost:3100 \\\nPAPERCLIP_API_KEY=\u003cpcp_board_...\u003e \\\nPAPERCLIP_COMPANY_ID=\u003ccompany-uuid\u003e \\\nnode paperclip/seed-agents.mjs\n```\n\nThe script POSTs or PATCHes a single `Hermes` agent per company with:\n- `runtimeConfig.heartbeat.enabled: false` (wake on demand, not on a timer)\n- `adapterConfig.env` pointing to the company-specific `HERMES_HOME` path\n- `capabilities` pointing the agent at the shared delegation protocol and org chart\n\nRe-running is safe: existing agents are patched, not duplicated.\n\n## Tool Access Seed\n\n`paperclip/seed-tool-access.mjs` seeds the company-level tool catalog and role presets used by Paperclip's tool access matrix. Profile-sync invokes it automatically after reconciling Hermes profiles when `TOOL_ACCESS_SEED_ENABLED=1`.\n\nThe default catalog contains Hermes adapter toolsets (`terminal`, `file`, `web`) and the bundled Paperclip MCP tools. The default `agent-stack-hermes-default` preset grants the same broad access the template historically gave Hermes agents, but routes it through Paperclip's auditable grant model when the API is available.\n\n```env\nTOOL_ACCESS_SEED_ENABLED=1\nTOOL_ACCESS_APPLY_DEFAULT_PRESET=1\nTOOL_ACCESS_DEFAULT_PRESET=agent-stack-hermes-default\n```\n\nRe-running is safe. The script creates missing tools and presets only, and it applies the default preset only when an active `hermes_local` agent does not already have the matching grants. On Paperclip builds without the tool access API it logs a skip and makes no changes.\n\nCompany selection follows the same scope as profile sync: leave `PAPERCLIP_COMPANY_IDS` blank to seed every accessible company, or set a comma-separated list such as `PAPERCLIP_COMPANY_IDS=co_123,co_456`.\n\nManual run:\n\n```bash\nPAPERCLIP_API_BASE=http://localhost:3100 \\\nPAPERCLIP_API_KEY=\u003cpcp_board_...\u003e \\\nPAPERCLIP_COMPANY_IDS=\u003ccompany-uuid\u003e \\\nnode paperclip/seed-tool-access.mjs\n```\n\n## 100M Framework Learning Loop\n\nThe bundled `use-100m-framework` skill is installed into every Hermes profile\nthrough the existing `hermes-runtime/skills` propagation path. Bootstrap and\nprofile-sync symlink the skill into each profile's `skills/agent-stack/`\ndirectory, so new Paperclip-managed profiles pick it up without separate setup.\nProfile sync also seeds it into Paperclip company skills by default, which makes\nPaperclip the control-plane source of truth for whether managed roles should use\nit.\n\nCompany agents use the skill to apply the shared `$100M` framework and write\nsanitized `100m-field-learning` proposals into their memories.\nPromotion is centralized: company profiles do not edit shared framework doctrine\ndirectly. See [docs/100m-framework-learning-loop.md](docs/100m-framework-learning-loop.md)\nfor the pull model, promotion classes, and personal-Hermes curator cron.\n\n## EOS Framework Runtime Skill\n\nThe bundled `use-eos-framework` skill is installed into every Hermes profile\nthrough the same `hermes-runtime/skills` propagation path. Agents use it to turn\nselected `$100M` opportunities into Rocks, owners, scorecards, Paperclip issue\ntrees, routine setup issues, and escalation paths.\n\nThe shared EOS doctrine stays outside this blank template at\n`00_resources/frameworks/eos-framework/`. Company agents write sanitized\n`eos-field-learning` proposals into their memories when work\nproduces reusable improvements. If the current Paperclip tool surface does not\ninclude routine creation, agents file a routine setup issue instead of claiming\nthat the routine exists.\n\n## Profile Sync \u0026 Org Chart\n\nThe `paperclip` container can run an embedded reconciliation loop that mirrors Paperclip's roster into per-role Hermes profiles and adapter skill-sync state. It is enabled in the generated Coolify env; set the API key after first-run auth:\n\n```env\nPROFILE_SYNC_ENABLED=1\nPROFILE_SYNC_INTERVAL_SEC=60\nPROFILE_SYNC_DELETE_MODE=archive\nPAPERCLIP_PROFILE_SYNC_API_KEY=\u003cpcp_board_...\u003e\nTOOL_ACCESS_SEED_ENABLED=1\nTOOL_ACCESS_APPLY_DEFAULT_PRESET=1\nTOOL_ACCESS_DEFAULT_PRESET=agent-stack-hermes-default\n```\n\nEvery `hermes_local` Paperclip agent gets:\n\n```text\nHermes profile: /data/hermes/profiles/\u003ccompany-role\u003e\nSkill sync:     adapterConfig.paperclipSkillSync.desiredSkills\nToolsets:       adapterConfig.toolsets includes terminal,file,web,mcp\n```\n\nThe profile slug is stored on the Paperclip agent's metadata, so company or role renames don't move existing profiles.\n\nNew profile homes inherit the default profile's reusable setup:\n- `/data/hermes/*` is copied except runtime profile/archive/cache/log folders\n\nWhen an agent disappears from a successfully-scanned company:\n- `archive` mode (default): folders move under `/data/hermes/archive/`\n- `purge` mode: permanent deletion\n\n**Org chart mirroring.** Each sync iteration also writes the current Paperclip org chart to:\n\n```text\n/data/agent-stack/org-chart.md     (human-readable, with reportsTo lines)\n/data/agent-stack/org-chart.json   (machine-readable, consumed by agents)\n```\n\nThe Delegation Protocol (below) tells agents to consult these before accepting, rerouting, or completing issues.\n\n**Optional company scoping** (default: every company the key has access to):\n\n```env\nPAPERCLIP_COMPANY_IDS=co_123,co_456\nPAPERCLIP_COMPANIES=co_123:Acme,co_456:Koenig\n```\n\nRun one sync manually from the running container:\n\n```bash\ndocker compose --env-file .env exec paperclip node /opt/paperclip/profile-sync.mjs once\n```\n\n### Cleaning up historical `canAssignTasks` drift\n\nThe steady-state revoke (see [Profile Sync \u0026 Org Chart](#profile-sync--org-chart)) only touches agents profile-sync recorded in its own manifest. Agents granted `canAssignTasks` by an earlier, more permissive code path — or by a manual click in the Paperclip UI — are not in the manifest and so are out of scope for the steady-state cleanup. For that one-time backfill, ship the deployment a one-shot script at `/opt/paperclip/narrow-grants.mjs`:\n\n```bash\n# Dry-run (default — lists candidates, makes no changes):\ndocker exec \u003cpaperclip-container\u003e sh -c '\n  PAPERCLIP_API_BASE=http://127.0.0.1:3100 \\\n  PAPERCLIP_API_KEY=$PAPERCLIP_API_KEY \\\n  node /opt/paperclip/narrow-grants.mjs'\n\n# Apply (only after eyeballing the dry-run output):\ndocker exec \u003cpaperclip-container\u003e sh -c '\n  PAPERCLIP_API_BASE=http://127.0.0.1:3100 \\\n  PAPERCLIP_API_KEY=$PAPERCLIP_API_KEY \\\n  node /opt/paperclip/narrow-grants.mjs --apply'\n```\n\nWhy the explicit `PAPERCLIP_API_BASE=http://127.0.0.1:3100` override: the container's default `PAPERCLIP_API_BASE` points at the docker-compose service hostname (`http://paperclip:3100`), which doesn't resolve back to self from within the same container the way the entrypoint-spawned profile-sync subprocess sees it. Setting `127.0.0.1` for ad-hoc invocations sidesteps that.\n\nThe script flags every agent that has `access.canAssignTasks=true \u0026\u0026 access.taskAssignSource='explicit_grant'` AND zero direct reports. CEOs (`source: ceo_role`) are skipped automatically because they don't match the `explicit_grant` filter. Revoke payload preserves the agent's existing `canCreateAgents` bit. If a candidate looks wrong (e.g. an \"Engineering Manager\" that *should* have reports but doesn't in the data), fix the `reportsTo` field in the Paperclip UI first — the steady-state reconcile will then re-grant on its next cycle.\n\n## Blank Image Audit\n\nAfter a local build, audit the image before publishing or reusing it:\n\n```bash\ndocker compose -f compose.yaml -f compose.build.yaml --env-file .env.example build\n./scripts/audit-blank-image.sh template-agent:local\n```\n\nThe audit fails if the image contains runtime state under `/data`, Lee/client deployment markers, Coolify build metadata, or token-looking secrets in image metadata.\n\n## Runtime Data\n\nThe Dockerfile deliberately cleans `/data` during build. Runtime data appears only after a container starts with the `paperclip-data` volume mounted.\n\nThe default Hermes config is intentionally minimal — only the Paperclip MCP server is wired in (see the \"Paperclip MCP Server\" section above). The template bootstraps neutral profile files into Hermes profiles for each synced role (see \"Profile Sync \u0026 Org Chart\" above).\n\n## Backups\n\nAll runtime state lives under `/data` inside the `paperclip-data` Docker volume. Back up that single volume and you can rebuild the stack on a fresh host.\n\nWhat's worth backing up:\n\n- `/data/instances/` — Paperclip companies, agents, kanban, sessions.\n- `/data/hermes/` — Hermes profiles, config, kanban DB.\n- `/data/agent-stack/` — protocols, org-chart, profile-sync state.\n\n### Recommended: nightly `restic` to Backblaze B2\n\nAdd a Coolify \"Scheduled Task\" on the Docker Compose app:\n\n- **Container:** `paperclip`\n- **Frequency:** `0 3 * * *` (daily at 03:00)\n- **Command:**\n\n```bash\nRESTIC_PASSWORD_FILE=/data/.restic-password \\\nRESTIC_REPOSITORY=b2:\u003cyour-bucket-name\u003e:\u003cdeploy-name\u003e/paperclip-data \\\nB2_ACCOUNT_ID=\u003ckeyID\u003e \\\nB2_ACCOUNT_KEY=\u003capplicationKey\u003e \\\nrestic --no-cache backup /data \\\n  --exclude /data/.locks \\\n  --exclude /data/.cache \\\n  --exclude '/data/**/node_modules' \\\n\u0026\u0026 restic --no-cache forget --prune \\\n  --keep-daily 7 --keep-weekly 4 --keep-monthly 12\n```\n\nBootstrap once before the first scheduled run:\n\n```bash\n# Inside the paperclip container:\necho '\u003clong random string\u003e' \u003e /data/.restic-password\nchmod 600 /data/.restic-password\nrestic init  # creates the encrypted repo in B2\n```\n\n### Restoring on a fresh deploy\n\n```bash\nRESTIC_PASSWORD_FILE=/data/.restic-password \\\nRESTIC_REPOSITORY=b2:\u003cyour-bucket-name\u003e:\u003cdeploy-name\u003e/paperclip-data \\\nB2_ACCOUNT_ID=\u003ckeyID\u003e \\\nB2_ACCOUNT_KEY=\u003capplicationKey\u003e \\\nrestic restore latest --target /\n```\n\nRestic is not yet bundled in the image. Either:\n\n1. Bake it into the Dockerfile (`apt-get install restic`).\n2. Or run it via a sidecar container in compose that mounts the same `paperclip-data` volume.\n\n### Alternative: bind-mount + host backup\n\nIf your Coolify host already has a backup tool (rclone, tarsnap, Time Machine, etc.) covering a host directory, you can convert `paperclip-data` from a named Docker volume to a bind mount of a directory on the host. Any backup of that host directory now captures the agent stack state without a per-container step. This suits a single-host deployment whose host directory is already covered by an external/OS-level backup.\n\n## Scripts\n\nEverything in `scripts/`:\n\n| Script | What it does |\n|---|---|\n| `local-up.sh` / `local-down.sh` / `local-logs.sh` | Bring up, tear down, or stream logs from the local docker-compose stack. |\n| `validate-env.sh` | Sanity-check `.env` for required keys and shape. Pass `--coolify` to enforce stricter rules for Coolify deployments. |\n| `coolify-env.sh \u003cdomain\u003e` | Generate a starter `.env` block for a brand domain. |\n| `render-coolify-compose.sh \u003cdomain\u003e \u003cproject-name\u003e` | Render `compose.yaml` with brand-specific Traefik labels and routes. |\n| `audit-blank-image.sh \u003cimage-tag\u003e` | Inspect a built image for tokens, client data, runtime state under `/data`, or Coolify metadata leaks. |\n| `test-blank-template.sh` | Verify the repo state is genuinely client-neutral (no committed `data/`, `instances/`, `.env`, etc.). |\n| `test-default-profile-only.sh` | Assert no extra Hermes profiles bleed into the built image. |\n| `test-hermes-tui-prebuilt.sh` | Confirm the Hermes TUI is prebuilt in the image (faster cold-start). |\n| `test-no-provider-placeholders.sh` | Catch `OPENAI_API_KEY=` / `ANTHROPIC_API_KEY=` / `OPENROUTER_API_KEY=` empty rows that would break the Hermes `/env` first-run flow. |\n\nRun the audit + tests after building a new image:\n\n```bash\ndocker compose -f compose.yaml -f compose.build.yaml --env-file .env.example build\n./scripts/audit-blank-image.sh template-agent:local\n./scripts/test-blank-template.sh\n./scripts/test-default-profile-only.sh\n./scripts/test-hermes-tui-prebuilt.sh\n./scripts/test-no-provider-placeholders.sh\n```\n\n## Important Information Index\n\nUse this shared runtime file as the human-maintained index for important client information:\n\n```text\n/data/agent-stack/important-information-index.md\n```\n\nThe `paperclip` service seeds the file if it does not already exist. It will not\noverwrite an existing index.\n\nKeep high-value pointers here: key Paperclip projects, source paths under `/data/instances`, credentials locations, decisions, client conventions, and anything agents should check before starting broad work. The index should point to durable sources rather than duplicating large content.\n\n## Learning Protocol\n\nThe stack includes a task-scoped learning protocol. It is not a background crawler.\n\nAt container startup, the `paperclip` service installs the shared protocol into:\n\n```text\n/data/agent-stack/learning-protocol.md\n```\n\nIt also mirrors the same protocol into the default Hermes profile at:\n\n```text\n/data/hermes/LEARNING_PROTOCOL.md\n```\n\nSynced role profile homes receive:\n\n```text\n/data/hermes/profiles/\u003ccompany-role\u003e/LEARNING_PROTOCOL.md\n```\n\nThe learning loop is:\n\n1. At task start, consult the shared protocols and index.\n2. Inspect only relevant Paperclip files under `/data/instances`.\n3. Use `/data/agent-stack/important-information-index.md` for high-value pointers.\n4. At task end, update the shared index if you discovered a valuable pointer.\n\n## Delegation Protocol\n\nThe stack includes a shared Paperclip/Hermes delegation protocol for multi-role teams.\nAt container startup, the `paperclip` service installs the protocol into the shared VM\nvolume at:\n\n```text\n/data/agent-stack/delegation-protocol.md\n```\n\nIt also mirrors the same contract into the default Hermes profile at:\n\n```text\n/data/hermes/DELEGATION_PROTOCOL.md\n```\n\nWhen `seed-agents.mjs` creates the default Hermes agent, and when `profile-sync.mjs`\npatches Paperclip `hermes_local` agents, each agent's `capabilities` field receives\na short pointer telling it to read the shared protocol and Paperclip org chart\nbefore accepting, rerouting, creating, commenting on, or completing issues. New\nHermes profile homes also receive `DELEGATION_PROTOCOL.md` as a fallback copy.\n\nDuring profile sync, all active Paperclip agents also receive a concise\n`Capability Discovery` clause if they do not already have one. That clause makes\nrole ownership and peer-manager routing explicit for cross-team delegation.\n\nTo reset a local test install:\n\n```bash\ndocker compose --env-file .env.example down -v\n```\n\nDo not commit generated runtime folders such as `data/`, `instances/`, or `hermes/`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleebaroneau%2Ftemplate-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleebaroneau%2Ftemplate-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleebaroneau%2Ftemplate-agent/lists"}