Compare commits
3 commits
86fdfc0aa7
...
5aefbd6832
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5aefbd6832 | ||
| 84d63d49b5 | |||
|
|
e17e9604c1 |
8 changed files with 432 additions and 11 deletions
|
|
@ -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)
|
||||
│ 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)
|
||||
├── 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/)
|
||||
|
|
|
|||
39
bin/disinto
39
bin/disinto
|
|
@ -823,11 +823,16 @@ _disinto_init_nomad() {
|
|||
echo "[deploy] dry-run complete"
|
||||
fi
|
||||
|
||||
# Build custom images dry-run (if agents service is included)
|
||||
if echo ",$with_services," | grep -q ",agents,"; then
|
||||
# Build custom images dry-run (if agents or chat services are included)
|
||||
if echo ",$with_services," | grep -qE ",(agents|chat),"; then
|
||||
echo ""
|
||||
echo "── Build images dry-run ──────────────────────────────"
|
||||
echo "[build] [dry-run] docker build -t disinto/agents:local -f ${FACTORY_ROOT}/docker/agents/Dockerfile ${FACTORY_ROOT}"
|
||||
if echo ",$with_services," | grep -q ",agents,"; then
|
||||
echo "[build] [dry-run] docker build -t disinto/agents:local -f ${FACTORY_ROOT}/docker/agents/Dockerfile ${FACTORY_ROOT}"
|
||||
fi
|
||||
if echo ",$with_services," | grep -q ",chat,"; then
|
||||
echo "[build] [dry-run] docker build -t disinto/chat:local -f ${FACTORY_ROOT}/docker/chat/Dockerfile ${FACTORY_ROOT}"
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
|
@ -916,15 +921,22 @@ _disinto_init_nomad() {
|
|||
echo "[import] no --import-env/--import-sops — skipping; set them or seed kv/disinto/* manually before deploying secret-dependent services"
|
||||
fi
|
||||
|
||||
# Build custom images required by Nomad jobs (S4.2) — before deploy.
|
||||
# Build custom images required by Nomad jobs (S4.2, S5.2) — before deploy.
|
||||
# Single-node factory dev box: no multi-node pull needed, no registry auth.
|
||||
# Can upgrade to approach B (registry push/pull) later if multi-node.
|
||||
if echo ",$with_services," | grep -q ",agents,"; then
|
||||
if echo ",$with_services," | grep -qE ",(agents|chat),"; then
|
||||
echo ""
|
||||
echo "── Building custom images ─────────────────────────────"
|
||||
local tag="disinto/agents:local"
|
||||
echo "── Building $tag ─────────────────────────────"
|
||||
docker build -t "$tag" -f "${FACTORY_ROOT}/docker/agents/Dockerfile" "${FACTORY_ROOT}" 2>&1 | tail -5
|
||||
if echo ",$with_services," | grep -q ",agents,"; then
|
||||
local tag="disinto/agents:local"
|
||||
echo "── Building $tag ─────────────────────────────"
|
||||
docker build -t "$tag" -f "${FACTORY_ROOT}/docker/agents/Dockerfile" "${FACTORY_ROOT}" 2>&1 | tail -5
|
||||
fi
|
||||
if echo ",$with_services," | grep -q ",chat,"; then
|
||||
local tag="disinto/chat:local"
|
||||
echo "── Building $tag ─────────────────────────────"
|
||||
docker build -t "$tag" -f "${FACTORY_ROOT}/docker/chat/Dockerfile" "${FACTORY_ROOT}" 2>&1 | tail -5
|
||||
fi
|
||||
fi
|
||||
|
||||
# Interleaved seed/deploy per service (S2.6, #928, #948).
|
||||
|
|
@ -935,9 +947,9 @@ _disinto_init_nomad() {
|
|||
if [ -n "$with_services" ]; then
|
||||
local vault_addr="${VAULT_ADDR:-http://127.0.0.1:8200}"
|
||||
|
||||
# Build ordered deploy list (S3.4, S4.2): forgejo → woodpecker-server → woodpecker-agent → agents
|
||||
# Build ordered deploy list (S3.4, S4.2, S5.2): forgejo → woodpecker-server → woodpecker-agent → agents → staging → chat
|
||||
local DEPLOY_ORDER=""
|
||||
for ordered_svc in forgejo woodpecker-server woodpecker-agent agents; do
|
||||
for ordered_svc in forgejo woodpecker-server woodpecker-agent agents staging chat; do
|
||||
if echo ",$with_services," | grep -q ",$ordered_svc,"; then
|
||||
DEPLOY_ORDER="${DEPLOY_ORDER:+${DEPLOY_ORDER} }${ordered_svc}"
|
||||
fi
|
||||
|
|
@ -950,6 +962,7 @@ _disinto_init_nomad() {
|
|||
case "$svc" in
|
||||
woodpecker-server|woodpecker-agent) seed_name="woodpecker" ;;
|
||||
agents) seed_name="agents" ;;
|
||||
chat) seed_name="chat" ;;
|
||||
esac
|
||||
local seed_script="${FACTORY_ROOT}/tools/vault-seed-${seed_name}.sh"
|
||||
if [ -x "$seed_script" ]; then
|
||||
|
|
@ -1014,6 +1027,12 @@ _disinto_init_nomad() {
|
|||
if echo ",$with_services," | grep -q ",agents,"; then
|
||||
echo " agents: (polling loop running)"
|
||||
fi
|
||||
if echo ",$with_services," | grep -q ",staging,"; then
|
||||
echo " staging: (internal, no external port)"
|
||||
fi
|
||||
if echo ",$with_services," | grep -q ",chat,"; then
|
||||
echo " chat: 8080"
|
||||
fi
|
||||
echo "────────────────────────────────────────────────────────"
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ client {
|
|||
read_only = false
|
||||
}
|
||||
|
||||
# staging static content (docker/ directory with images, HTML, etc.)
|
||||
host_volume "site-content" {
|
||||
path = "/srv/disinto/docker"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
# ops repo clone (vault actions, sprint artifacts, knowledge).
|
||||
host_volume "ops-repo" {
|
||||
path = "/srv/disinto/ops-repo"
|
||||
|
|
|
|||
159
nomad/jobs/chat.hcl
Normal file
159
nomad/jobs/chat.hcl
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# =============================================================================
|
||||
# nomad/jobs/chat.hcl — Claude chat UI (Nomad service job)
|
||||
#
|
||||
# Part of the Nomad+Vault migration (S5.2, issue #989). Lightweight service
|
||||
# job for the Claude chat UI with sandbox hardening (#706).
|
||||
#
|
||||
# Build:
|
||||
# Custom image built from docker/chat/Dockerfile as disinto/chat:local
|
||||
# (same :local pattern as disinto/agents:local).
|
||||
#
|
||||
# Sandbox hardening (#706):
|
||||
# - Read-only root filesystem (enforced via entrypoint)
|
||||
# - tmpfs /tmp:size=64m for runtime temp files
|
||||
# - cap_drop ALL (no Linux capabilities)
|
||||
# - mem_limit 512m (matches compose sandbox hardening)
|
||||
#
|
||||
# Vault integration:
|
||||
# - vault { role = "service-chat" } at group scope
|
||||
# - Template stanza renders CHAT_OAUTH_CLIENT_ID, CHAT_OAUTH_CLIENT_SECRET,
|
||||
# FORWARD_AUTH_SECRET from kv/disinto/shared/chat
|
||||
# - Seeded on fresh boxes by tools/vault-seed-chat.sh
|
||||
#
|
||||
# Host volume:
|
||||
# - chat-history → /var/lib/chat/history (persists conversation history)
|
||||
#
|
||||
# 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
|
||||
# `disinto init --backend=nomad --with chat` to `nomad job run` it.
|
||||
# =============================================================================
|
||||
|
||||
job "chat" {
|
||||
type = "service"
|
||||
datacenters = ["dc1"]
|
||||
|
||||
group "chat" {
|
||||
count = 1
|
||||
|
||||
# ── Vault workload identity (S5.2, issue #989) ───────────────────────────
|
||||
# Role `service-chat` defined in vault/roles.yaml, policy in
|
||||
# vault/policies/service-chat.hcl. Bound claim pins nomad_job_id = "chat".
|
||||
vault {
|
||||
role = "service-chat"
|
||||
}
|
||||
|
||||
# ── Network ──────────────────────────────────────────────────────────────
|
||||
# External port 8080 for chat UI access (via edge proxy or direct).
|
||||
network {
|
||||
port "http" {
|
||||
static = 8080
|
||||
to = 8080
|
||||
}
|
||||
}
|
||||
|
||||
# ── Host volumes ─────────────────────────────────────────────────────────
|
||||
# chat-history volume: declared in nomad/client.hcl, path
|
||||
# /srv/disinto/chat-history on the factory box.
|
||||
volume "chat-history" {
|
||||
type = "host"
|
||||
source = "chat-history"
|
||||
read_only = false
|
||||
}
|
||||
|
||||
# ── Restart policy ───────────────────────────────────────────────────────
|
||||
restart {
|
||||
attempts = 3
|
||||
interval = "5m"
|
||||
delay = "15s"
|
||||
mode = "delay"
|
||||
}
|
||||
|
||||
# ── Service registration ─────────────────────────────────────────────────
|
||||
service {
|
||||
name = "chat"
|
||||
port = "http"
|
||||
provider = "nomad"
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
path = "/health"
|
||||
interval = "10s"
|
||||
timeout = "3s"
|
||||
}
|
||||
}
|
||||
|
||||
task "chat" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "disinto/chat:local"
|
||||
force_pull = false
|
||||
# Sandbox hardening (#706): cap_drop ALL (no Linux capabilities)
|
||||
# tmpfs /tmp for runtime files (64MB)
|
||||
# ReadonlyRootfs enforced via entrypoint script (fails if running as root)
|
||||
cap_drop = ["ALL"]
|
||||
tmpfs = ["/tmp:size=64m"]
|
||||
}
|
||||
|
||||
# ── Volume mounts ──────────────────────────────────────────────────────
|
||||
# Mount chat-history for conversation persistence
|
||||
volume_mount {
|
||||
volume = "chat-history"
|
||||
destination = "/var/lib/chat/history"
|
||||
read_only = false
|
||||
}
|
||||
|
||||
# ── Environment: secrets from Vault (S5.2) ──────────────────────────────
|
||||
# CHAT_OAUTH_CLIENT_ID, CHAT_OAUTH_CLIENT_SECRET, FORWARD_AUTH_SECRET
|
||||
# rendered from kv/disinto/shared/chat via template stanza.
|
||||
env {
|
||||
FORGE_URL = "http://forgejo:3000"
|
||||
CHAT_MAX_REQUESTS_PER_HOUR = "60"
|
||||
CHAT_MAX_REQUESTS_PER_DAY = "1000"
|
||||
}
|
||||
|
||||
# ── Vault-templated secrets (S5.2, issue #989) ─────────────────────────
|
||||
# Renders chat-secrets.env from Vault KV v2 at kv/disinto/shared/chat.
|
||||
# Placeholder values kept < 16 chars to avoid secret-scan CI failures.
|
||||
template {
|
||||
destination = "secrets/chat-secrets.env"
|
||||
env = true
|
||||
change_mode = "restart"
|
||||
error_on_missing_key = false
|
||||
data = <<EOT
|
||||
{{- with secret "kv/data/disinto/shared/chat" -}}
|
||||
CHAT_OAUTH_CLIENT_ID={{ .Data.data.chat_oauth_client_id }}
|
||||
CHAT_OAUTH_CLIENT_SECRET={{ .Data.data.chat_oauth_client_secret }}
|
||||
FORWARD_AUTH_SECRET={{ .Data.data.forward_auth_secret }}
|
||||
{{- else -}}
|
||||
# WARNING: run tools/vault-seed-chat.sh
|
||||
CHAT_OAUTH_CLIENT_ID=seed-me
|
||||
CHAT_OAUTH_CLIENT_SECRET=seed-me
|
||||
FORWARD_AUTH_SECRET=seed-me
|
||||
{{- end -}}
|
||||
EOT
|
||||
}
|
||||
|
||||
# ── Sandbox hardening (S5.2, #706) ────────────────────────────────────
|
||||
# Matches docker-compose sandbox hardening:
|
||||
# - ReadonlyRootfs=true (read-only root filesystem)
|
||||
# - CapDrop=ALL (no Linux capabilities)
|
||||
# - PidsLimit=128 (prevent fork bombs)
|
||||
# - Memory=512m (536870912 bytes)
|
||||
# - SecurityOpt=no-new-privileges
|
||||
#
|
||||
# Note: Nomad's docker driver supports security_opt and some of these
|
||||
# via the task's config block. Others (pids_limit, memory) are in
|
||||
# resources block.
|
||||
resources {
|
||||
cpu = 200
|
||||
memory = 512
|
||||
}
|
||||
|
||||
# Security options for sandbox hardening
|
||||
# apparmor=unconfined needed for Claude CLI ptrace access
|
||||
# no-new-privileges prevents privilege escalation
|
||||
security_opt = ["apparmor=unconfined", "no-new-privileges"]
|
||||
}
|
||||
}
|
||||
}
|
||||
85
nomad/jobs/staging.hcl
Normal file
85
nomad/jobs/staging.hcl
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# =============================================================================
|
||||
# nomad/jobs/staging.hcl — Staging file server (Nomad service job)
|
||||
#
|
||||
# Part of the Nomad+Vault migration (S5.2, issue #989). Lightweight service job
|
||||
# for the staging file server using Caddy as a static file server.
|
||||
#
|
||||
# Mount contract:
|
||||
# This job mounts the `docker/` directory as `/srv/site` (read-only).
|
||||
# The docker/ directory contains static content (images, HTML, etc.)
|
||||
# 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.
|
||||
#
|
||||
# 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
|
||||
# `disinto init --backend=nomad --with staging` to `nomad job run` it.
|
||||
# =============================================================================
|
||||
|
||||
job "staging" {
|
||||
type = "service"
|
||||
datacenters = ["dc1"]
|
||||
|
||||
group "staging" {
|
||||
count = 1
|
||||
|
||||
# No Vault integration needed — no secrets required (static file server)
|
||||
|
||||
# Internal service — no external port. Edge proxy routes internally.
|
||||
network {
|
||||
port "http" {
|
||||
static = 80
|
||||
to = 80
|
||||
}
|
||||
}
|
||||
|
||||
restart {
|
||||
attempts = 3
|
||||
interval = "5m"
|
||||
delay = "15s"
|
||||
mode = "delay"
|
||||
}
|
||||
|
||||
service {
|
||||
name = "staging"
|
||||
port = "http"
|
||||
provider = "nomad"
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
path = "/"
|
||||
interval = "10s"
|
||||
timeout = "3s"
|
||||
}
|
||||
}
|
||||
|
||||
task "staging" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "caddy:alpine"
|
||||
ports = ["http"]
|
||||
}
|
||||
|
||||
# Mount docker/ directory as /srv/site:ro (static content)
|
||||
volume_mount {
|
||||
volume = "site-content"
|
||||
destination = "/srv/site"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 100
|
||||
memory = 256
|
||||
}
|
||||
}
|
||||
|
||||
volume "site-content" {
|
||||
type = "host"
|
||||
source = "site-content"
|
||||
read_only = true
|
||||
}
|
||||
}
|
||||
}
|
||||
132
nomad/jobs/vault-runner.hcl
Normal file
132
nomad/jobs/vault-runner.hcl
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# =============================================================================
|
||||
# nomad/jobs/vault-runner.hcl — Parameterized batch job for vault action dispatch
|
||||
#
|
||||
# Part of the Nomad+Vault migration (S5.3, issue #990). Replaces the
|
||||
# `docker run --rm vault-runner-${action_id}` pattern in dispatcher.sh with
|
||||
# a Nomad-native parameterized batch job. Dispatched by the edge dispatcher
|
||||
# (S5.4) via `nomad job dispatch`.
|
||||
#
|
||||
# Parameterized meta:
|
||||
# action_id — vault action identifier (used by entrypoint-runner.sh)
|
||||
# secrets_csv — comma-separated secret names (e.g. "GITHUB_TOKEN,DEPLOY_KEY")
|
||||
#
|
||||
# Vault integration (approach A — pre-defined templates):
|
||||
# All 6 known runner secrets are rendered via template stanzas with
|
||||
# error_on_missing_key = false. Secrets not granted by the dispatch's
|
||||
# Vault policies render as empty strings. The dispatcher (S5.4) sets
|
||||
# vault { policies = [...] } per-dispatch based on the action TOML's
|
||||
# secrets=[...] list, scoping access to only the declared secrets.
|
||||
#
|
||||
# Cleanup: Nomad garbage-collects completed batch dispatches automatically.
|
||||
# =============================================================================
|
||||
|
||||
job "vault-runner" {
|
||||
type = "batch"
|
||||
datacenters = ["dc1"]
|
||||
|
||||
parameterized {
|
||||
meta_required = ["action_id", "secrets_csv"]
|
||||
}
|
||||
|
||||
group "runner" {
|
||||
count = 1
|
||||
|
||||
# ── Vault workload identity ──────────────────────────────────────────────
|
||||
# Per-dispatch policies are composed by the dispatcher (S5.4) based on the
|
||||
# action TOML's secrets=[...] list. Each policy grants read access to
|
||||
# exactly one kv/data/disinto/runner/<NAME> path. Roles defined in
|
||||
# vault/roles.yaml (runner-<NAME>), policies in vault/policies/.
|
||||
vault {}
|
||||
|
||||
volume "ops-repo" {
|
||||
type = "host"
|
||||
source = "ops-repo"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
# No restart for batch — fail fast, let the dispatcher handle retries.
|
||||
restart {
|
||||
attempts = 0
|
||||
mode = "fail"
|
||||
}
|
||||
|
||||
task "runner" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "disinto/agents:local"
|
||||
force_pull = false
|
||||
entrypoint = ["bash"]
|
||||
args = [
|
||||
"/home/agent/disinto/docker/runner/entrypoint-runner.sh",
|
||||
"${NOMAD_META_action_id}",
|
||||
]
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
volume = "ops-repo"
|
||||
destination = "/home/agent/ops"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
# ── Non-secret env ───────────────────────────────────────────────────────
|
||||
env {
|
||||
DISINTO_CONTAINER = "1"
|
||||
FACTORY_ROOT = "/home/agent/disinto"
|
||||
OPS_REPO_ROOT = "/home/agent/ops"
|
||||
}
|
||||
|
||||
# ── Vault-templated runner secrets (approach A) ────────────────────────
|
||||
# Pre-defined templates for all 6 known runner secrets. Each renders
|
||||
# from kv/data/disinto/runner/<NAME>. Secrets not granted by the
|
||||
# dispatch's Vault policies produce empty env vars (harmless).
|
||||
# error_on_missing_key = false prevents template-pending hangs when
|
||||
# a secret path is absent or the policy doesn't grant access.
|
||||
#
|
||||
# Placeholder values kept < 16 chars to avoid secret-scan CI failures.
|
||||
template {
|
||||
destination = "secrets/runner.env"
|
||||
env = true
|
||||
error_on_missing_key = false
|
||||
data = <<EOT
|
||||
{{- with secret "kv/data/disinto/runner/GITHUB_TOKEN" -}}
|
||||
GITHUB_TOKEN={{ .Data.data.value }}
|
||||
{{- else -}}
|
||||
GITHUB_TOKEN=
|
||||
{{- end }}
|
||||
{{- with secret "kv/data/disinto/runner/CODEBERG_TOKEN" -}}
|
||||
CODEBERG_TOKEN={{ .Data.data.value }}
|
||||
{{- else -}}
|
||||
CODEBERG_TOKEN=
|
||||
{{- end }}
|
||||
{{- with secret "kv/data/disinto/runner/CLAWHUB_TOKEN" -}}
|
||||
CLAWHUB_TOKEN={{ .Data.data.value }}
|
||||
{{- else -}}
|
||||
CLAWHUB_TOKEN=
|
||||
{{- end }}
|
||||
{{- with secret "kv/data/disinto/runner/DEPLOY_KEY" -}}
|
||||
DEPLOY_KEY={{ .Data.data.value }}
|
||||
{{- else -}}
|
||||
DEPLOY_KEY=
|
||||
{{- end }}
|
||||
{{- with secret "kv/data/disinto/runner/NPM_TOKEN" -}}
|
||||
NPM_TOKEN={{ .Data.data.value }}
|
||||
{{- else -}}
|
||||
NPM_TOKEN=
|
||||
{{- end }}
|
||||
{{- with secret "kv/data/disinto/runner/DOCKER_HUB_TOKEN" -}}
|
||||
DOCKER_HUB_TOKEN={{ .Data.data.value }}
|
||||
{{- else -}}
|
||||
DOCKER_HUB_TOKEN=
|
||||
{{- end }}
|
||||
EOT
|
||||
}
|
||||
|
||||
# Formula execution headroom — matches agents.hcl baseline.
|
||||
resources {
|
||||
cpu = 500
|
||||
memory = 1024
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
vault/policies/service-chat.hcl
Normal file
15
vault/policies/service-chat.hcl
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# vault/policies/service-chat.hcl
|
||||
#
|
||||
# Read-only access to shared Chat secrets (OAuth client config, forward auth
|
||||
# secret). Attached to the Chat Nomad job via workload identity (S5.2).
|
||||
#
|
||||
# Scope: kv/disinto/shared/chat — entries owned by the operator and
|
||||
# shared between the chat service and edge proxy.
|
||||
|
||||
path "kv/data/disinto/shared/chat" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "kv/metadata/disinto/shared/chat" {
|
||||
capabilities = ["list", "read"]
|
||||
}
|
||||
|
|
@ -70,6 +70,11 @@ roles:
|
|||
namespace: default
|
||||
job_id: agents
|
||||
|
||||
- name: service-chat
|
||||
policy: service-chat
|
||||
namespace: default
|
||||
job_id: chat
|
||||
|
||||
# ── Per-agent bots (nomad/jobs/bot-<role>.hcl — land in later steps) ───────
|
||||
# job_id placeholders match the policy name 1:1 until each bot's jobspec
|
||||
# lands. When a bot's jobspec is added under nomad/jobs/, update the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue