Deploy Hermes Slack Agent
Use this skill as an interactive deployment operator, not as a static checklist. Keep the conversation moving one missing input or one manual task at a time.
Core Contract
Source of truth:
agents/<name>/<name>.yamlin the user's project (created byinitor written by hand).Distribution: GitHub only — not on the npm registry. Install with
npm install github:truefoundry/tfy-hermes-agent.Two CLIs required: tfy (
pip install -U "truefoundry") applies manifests; tfy-hermes-agent compiles them and orchestratestfy apply -f.On-disk layout (per agent):
agents/<name>/ ├── <name>.yaml # edit this — source config ├── slack-app-manifest.json # Slack setup only (skipped with init --api-only) ├── .hermes-secrets.local # gitignored HERMES-RUN-TOKEN-SECRET seed └── deployments/ # compiled TF manifests (written by deploy) ├── <name>-volume.yaml ├── <name>-controller.yaml ├── <name>-executor.yaml └── <name>-artifact-cleanup.yaml # only when slack_inbound_artifact_repo is setGenerated output:
deploycompiles toagents/<name>/deployments/and runstfy apply -fon each file in order (volume → controller → executor, plus artifact cleanup when Slack artifact storage is enabled). Pass--emit-manifests <dir>to write elsewhere. SecretGroup is provisioned via API, not as a deployments file.Architecture: Slack deployments use one Slack app per agent; API-only (
init --api-only) skips Slack. Both paths use asecretsSecretGroup (deployauto-creates if missing) and three TrueFoundry resources —volume(RWO PVC mounted at /data on the controller),controller(Service), andexecutor(Job fortruefoundry-job, or internal Service fortruefoundry-service+ Daytona terminal sandbox). State durability is the controller's RWO/datavolume; offsite snapshotting is out of scope for the deployed stack.Slack transport: HTTP Events API and Interactivity only. Do not use Socket Mode, WebSockets, slash commands, Slack user groups, or Slack OAuth.
Slack files: The controller downloads Slack files with the bot token, uploads them to TrueFoundry Artifacts, and passes artifact read URLs to the executor. The executor downloads files into its workspace before Hermes starts. Images are passed as Hermes image inputs; other file types are referenced in the prompt by
local_path. Whenslack_inbound_artifact_repois configured, the generated stack includes a weekly artifact cleanup job that deletes only old Hermes Slack run artifacts. The cleanup job must useHERMES-ARTIFACT-CLEANUP-TFY-API-KEY, preferably a virtual-account token scoped to the inbound artifact ML repo, rather than the controller/executorTFY-API-KEY. Configureslack_inbound_artifact_cleanup.failure_alertonly when the tenant already has a valid TrueFoundry notification-channel integration FQN; otherwise omit it so manifests continue to apply.Secrets: never ask the user to paste raw Slack tokens, signing secrets, TrueFoundry API keys, or the HERMES run-token secret into chat.
deploysetsHERMES-RUN-TOKEN-SECRET(fromagents/<name>/.hermes-secrets.local) andTFY-API-KEYautomatically. For Slack, the user pastes bot token + signing secret into the SecretGroup UI after deploy — the only manual secret step.Deployment gate:
deploycallsensureSecretGroup(API) then runs live validation unless--skip-live-checksis passed. There is no separatevalidatecommand.
Load References
- Read
references/deployment-example.mdwhen creating or reviewing agent config, compiled manifests indeployments/, Slack app setup, deploy (including auto-provisioned secrets), or failure handling. - Read
references/session-smoke-test.mdbefore declaring a deployment healthy or when debugging runtime behavior.
Workflow
Track this sequence and resume from the first incomplete step.
Step 0 — Prerequisites
Hard stop until these are satisfied:
Runtime versions: Node 22+ (tfy-hermes-agent), Python 3.9+ (tfy CLI).
tfy CLI installed and authenticated:
pip install -U "truefoundry" tfy login --host https://<tenant>.truefoundry.cloudOr
TFY_HOST+TFY_API_KEYin the shell (overridescredentials.json). If~/.truefoundry/credentials.jsonis missing, stop and have the user runtfy login. For production agents:tfy login --host <url> --api-key <virtual-account-pat>withapplication:read+application:trigger.tfy-hermes-agent installed in the user's project or globally:
npm install github:truefoundry/tfy-hermes-agent # or: npm install -g github:truefoundry/tfy-hermes-agentCommands below use
tfy-hermes-agent. If installed locally, usenpx tfy-hermes-agentinstead.Verify:
tfy versionandtfy-hermes-agent help.
Step 1 — Agent config
If agents/<name>/<name>.yaml does not exist:
tfy-hermes-agent init
# API-only: tfy-hermes-agent init --api-only
Creates agents/<name>/ with <name>.yaml, slack-app-manifest.json (unless --api-only), .hermes-secrets.local, and an empty deployments/ folder.
Init wizard prompts
Required: name, description, model, workspace_fqn, gateway_url, secrets (SecretGroup name only — deploy creates it via API).
Then executor backend (wizard prompts after required fields; default truefoundry-job):
| Choice | Yaml | When to pick |
|---|---|---|
| Job (default) | omit executor, or executor: truefoundry-job |
Per-turn TF Job. Hermes tools run in the job container. No Daytona secret. |
| Service + Daytona | executor: truefoundry-service and terminal: { backend: daytona } |
Long-lived executor Service; tool sandbox in Daytona. Needs DAYTONA-API-KEY in SecretGroup after deploy. |
If the yaml was not created via init and executor is unset, ask before deploy unless the user already chose in this session.
Then optional (Enter to skip each; omitted from the yaml when blank):
| Field | Wizard prompt |
|---|---|
executor |
Job vs Service + Daytona (default job; service writes executor + terminal to yaml) |
version |
Git ref for image build (default main, omitted if unchanged) |
host |
Public controller URL |
instructions |
Multiline system prompt |
skills |
Comma-separated version-pinned FQNs |
mcp_servers |
Comma-separated MCP Gateway URLs |
slack_team_id |
Slack team id (T…) — skipped with --api-only |
slack.allowed_channels |
Comma-separated channel IDs — skipped with --api-only |
slack.allowed_users |
Comma-separated user IDs — skipped with --api-only |
Or write agents/<name>/<name>.yaml by hand using Input Rules below and references/deployment-example.md.
If it already exists, collect any missing fields and edit agents/<name>/<name>.yaml in place. See Input Rules below and references/deployment-example.md.
Step 2 — Slack app (skip if API-only)
Stop for manual Slack work before deploy:
- Go to https://api.slack.com/apps → Create New App → From an app manifest → paste
agents/<name>/slack-app-manifest.jsonfrominit. - Install App to the workspace.
- Copy bot token and signing secret (paste into SecretGroup after deploy in step 3):
- OAuth & Permissions → Bot User OAuth Token →
SLACK-BOT-TOKEN - Basic Information → Signing Secret →
SLACK-SIGNING-SECRET
- OAuth & Permissions → Bot User OAuth Token →
Step 3 — Deploy
No separate SecretGroup step. deploy auto-creates the group via API and sets HERMES-RUN-TOKEN-SECRET (from agents/<name>/.hermes-secrets.local) and TFY-API-KEY (from credentials.json or env). For Slack, the manual secret work is pasting tokens from step 2 into SLACK-BOT-TOKEN and SLACK-SIGNING-SECRET after deploy runs. If Slack artifact cleanup is enabled, set HERMES-ARTIFACT-CLEANUP-TFY-API-KEY to a virtual-account token scoped to the inbound artifact ML repo.
Stop only if secret-store integration discovery fails (then create the SecretGroup manually in the UI).
Preview manifests offline (compile only — no API secret provisioning, no apply):
tfy-hermes-agent deploy <name> --skip-live-checks
Compiles to agents/<name>/deployments/:
| File | Resource |
|---|---|
<name>-volume.yaml |
Volume (10Gi RWO, /data on controller) |
<name>-controller.yaml |
Service (Slack + /v1/*) |
<name>-executor.yaml |
Job or Service |
Apply to TrueFoundry (ensureSecretGroup via API first, then live validation, then tfy apply -f from deployments/ in order: volume → controller → executor):
tfy-hermes-agent deploy <name>
# or: tfy-hermes-agent deploy agents/<name>/<name>.yaml
Reads ~/.truefoundry/credentials.json from tfy login when TFY_HOST / TFY_API_KEY are unset.
Flags:
--update— overwrite an existing deployment of the same name in the workspace--emit-manifests <dir>— write compiled yaml to a custom directory instead ofagents/<name>/deployments/--skip-live-checks— compile only; does not apply or provision secrets
After a git-source image change, rebuild with:
tfy deploy --force -f agents/<name>/deployments/<name>-controller.yaml
tfy deploy --force -f agents/<name>/deployments/<name>-executor.yaml
(not plain tfy apply).
Step 4 — Post-deploy Slack URLs (skip if API-only)
After deploy, stop and have the user confirm Slack settings match the controller host:
https://<host>/slack/eventshttps://<host>/slack/interactions
Invite the bot to target channels.
Step 5 — Verify
All deployments:
curl -fsS https://<host>/api/health
curl -fsS -H "Authorization: Bearer <TFY-API-KEY>" https://<host>/v1/models
Slack deployments (skip if API-only):
curl -fsS https://<host>/slack/health
Then run backend session smoke tests (references/session-smoke-test.md). For Slack, finish with one real mention.
What deploy Validates Live
deploy always runs live unless --skip-live-checks is passed. Credentials come from TFY_HOST + TFY_API_KEY (env) or ~/.truefoundry/credentials.json (host, access_token). Every check must pass before manifests apply.
Auth & access:
- Credentials authenticate against the target host.
workspace_fqnexists and the key has access to it.
Naming & routing:
namedoes not clash with an existing deployment in the workspace. A match on the current agent's own deployment is an update, allowed only with--update.host(if specified) is not already routed to another deployment.
Resource references:
- Every
skillsFQN exists in the agent-skill registry and is fetchable with the key. - Every
skillsFQN is version-pinned (e.g.:1); floating tags are rejected. - Every
mcp_serversURL is a TrueFoundry MCP Gateway URL. - Every
mcp_serversURL resolves and is reachable with the key. modelis in the model list reachable from the key.
Secrets:
ensureSecretGroupruns insidedeployvia API — creates the SecretGroup when missing, setsHERMES-RUN-TOKEN-SECRETandTFY-API-KEYautomatically. Slack keys start as placeholders; user pastes real tokens after deploy if Slack is in scope. When Slack artifact cleanup is enabled,HERMES-ARTIFACT-CLEANUP-TFY-API-KEYmust also exist and should be scoped to the inbound artifact ML repo.- For
executor: truefoundry-service,DAYTONA-API-KEYmust also be set in the SecretGroup before tool-using turns work. Remind the user after deploy if they chose service mode.
Input Rules
| Field | Required | Notes |
|---|---|---|
name |
yes | Lowercase Slack-safe handle, 2–32 chars, e.g. devrel-assistant. Becomes the agents/<name>/ folder name. |
workspace_fqn |
yes | cluster:workspace, e.g. tfy-ea-dev-eo-az:sai-ws. |
gateway_url |
yes | OpenAI-compatible gateway URL for Hermes model calls. |
model |
yes | Gateway model id, e.g. openai-main/gpt-5.5. |
secrets |
yes | SecretGroup name (default <name>-hermes-secrets); deploy creates and populates TFY-API-KEY + HERMES-RUN-TOKEN-SECRET via API. Init only asks for the name. |
version |
no | Git ref for controller/executor image build. Default main. Slashed branches fail on TF's git puller — use commit SHA. |
host |
no | Public controller URL; inferred from TFY_HOST + name + workspace if omitted. |
description |
no | Short agent description. |
instructions |
no | System prompt appended each executor turn. |
slack.allowed_channels |
no | Channel/group/DM IDs. Omitted = all channels bot is in. |
slack.allowed_users |
no | User IDs. Omitted = all users. |
slack_team_id |
no | Slack team id if pinning a workspace. |
skills |
no | Version-pinned FQNs, e.g. agent-skill:tfy-eo/sai-mlrepo/humanizer:1. |
mcp_servers |
no | TrueFoundry MCP Gateway URLs only. |
executor |
no | truefoundry-job (default, per-turn Job) or truefoundry-service (Service + Daytona). init prompts; omit yaml field for job default. |
terminal |
no | Only for truefoundry-service; defaults to daytona. init writes this when service is chosen. |
If the user gives names instead of FQNs, URLs, or Slack IDs, pause and ask for exact values or offer to look them up when tooling is available. Prefer tfy-hermes-agent init when starting from scratch — it prompts every optional field.
Auth Model
| Surface | Credential | SecretGroup key |
|---|---|---|
/v1/* |
Bearer | TFY-API-KEY |
/api/internal/* |
Per-run HMAC | minted from HERMES-RUN-TOKEN-SECRET |
/slack/* webhooks |
Slack signature | SLACK-SIGNING-SECRET |
| Slack outbound | Bot token | SLACK-BOT-TOKEN |
| LLM gateway (executor) | Bearer | TFY-API-KEY |
Manual Stops
Only stop for external work:
- Installing/authenticating
tfy(tfy login) ortfy-hermes-agent - Production Virtual Account PAT setup (
tfy login --api-key …) when the user's personal token lacksapplication:read+application:trigger - Manual SecretGroup creation only when
deploycannot discover a secret-store integration - Slack app at https://api.slack.com/apps (skip if API-only) — before deploy; manifest at
agents/<name>/slack-app-manifest.json - Slack token entry in SecretGroup UI after deploy (never in chat; only manual secret step)
DAYTONA-API-KEYin SecretGroup whenexecutor: truefoundry-service(never in chat)- Slack URL verification in Slack settings (skip if API-only)
- First real Slack message result (skip if API-only)
When stopping, give exactly one concrete task and the file/path/name the user needs (e.g. agents/my-bot/slack-app-manifest.json, not a generic filename).
Completion Criteria
The flow is complete only when:
- Both CLIs installed; TrueFoundry credentials available via
tfy loginor env vars agents/<name>/<name>.yamlexists anddeploysucceeds (API secrets + live validation +tfy apply -fon volume, controller, executor indeployments/)TFY-API-KEYandHERMES-RUN-TOKEN-SECRETset bydeploy(no manual step)- Slack tokens pasted into SecretGroup after deploy (if Slack is in scope)
HERMES-ARTIFACT-CLEANUP-TFY-API-KEYset to a scoped virtual-account token when Slack artifact cleanup is enabled/api/healthand/v1/modelsrespond/slack/healthresponds with configured tokens (if Slack is in scope)- Backend session smoke tests pass (see
references/session-smoke-test.md) - Slack Events and Interactivity URLs verified in Slack settings (if Slack is in scope)
- A real Slack mention receives a final Hermes response (if Slack is in scope)