feat: profile-gated workspace-maintenance service and connector override generator (P2)

This commit is contained in:
2026-08-11 18:40:11 +02:00
parent 17f2e48463
commit c5f65d0f15
9 changed files with 156 additions and 19 deletions
+54
View File
@@ -55,6 +55,60 @@ services:
networks:
- thothii
workspace-maintenance:
image: thothii-core:local
profiles: [workspace-maintenance]
pull_policy: never
entrypoint: ["/usr/bin/tini", "--", "/app/docker/workspace-maintenance-entrypoint.sh"]
environment:
THT_HARNESS_DIR: /app/harness
THT_BIN: /opt/venv/bin/tht
THT_DATA_ROOT: /data
THT_WORKSPACE_REGISTRY_ROOT: /data/workspace-registry
THT_WORKSPACE_GIT_REMOTE: ${THT_WORKSPACE_GIT_REMOTE:?set THT_WORKSPACE_GIT_REMOTE}
THT_WORKSPACE_GIT_BRANCH: ${THT_WORKSPACE_GIT_BRANCH:-main}
THT_WORKSPACE_INSTALLATION_ID: ${THT_WORKSPACE_INSTALLATION_ID:-local}
THT_WORKSPACE_SECRET_ROOTS: /run/secrets
THT_SECRETS_FILE: /run/secrets/thothii.secrets
THT_DB_NAME: ${THT_DB_NAME:-}
THT_DWH_REST_URL: ${THT_DWH_REST_URL:-}
THT_LLM_URL: ${THT_LLM_URL:-}
THT_INTERNAL_QDRANT_URL: http://qdrant:6333
THT_INTERNAL_EMBEDDING_URL: http://embedding:11434
THT_INTERNAL_EMBEDDING_MODEL: qwen3-embedding:0.6b
THT_INTERNAL_EMBEDDING_DIMENSIONS: "1024"
HOME: /tmp/thoth
AWS_ACCESS_KEY_ID: ""
AWS_SECRET_ACCESS_KEY: ""
AWS_SESSION_TOKEN: ""
AWS_PROFILE: ""
AWS_DEFAULT_PROFILE: ""
AWS_CONFIG_FILE: /dev/null
AWS_SHARED_CREDENTIALS_FILE: /dev/null
volumes:
- type: volume
source: workspace-registry
target: /data/workspace-registry
read_only: true
- type: volume
source: sessions
target: /data/sessions
secrets:
- source: thothii_secrets
target: thothii.secrets
user: "10001:10001"
read_only: true
tmpfs:
- /tmp:rw,noexec,nosuid,nodev,size=64m,mode=1777
- /var/tmp:rw,noexec,nosuid,nodev,size=32m,mode=1777
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
restart: "no"
networks:
- thothii
frontend:
build:
context: .
+1
View File
@@ -1,5 +1,6 @@
# Select this override only for an HTTPS Git remote. The separate CA mount keeps TLS validation
# explicit; neither host-only source file nor its contents belongs in the base Compose contract.
# Active-snapshot workspace-maintenance operations intentionally receive no Git credential mounts.
x-thoth-git-transport: https
services:
+1
View File
@@ -1,5 +1,6 @@
# Select this override only for an SSH Git remote. The host-only source files must be absolute,
# normalized paths; strict host-key checking is mandatory for registry pull and publish.
# Active-snapshot workspace-maintenance operations intentionally receive no Git credential mounts.
x-thoth-git-transport: ssh
services:
+5
View File
@@ -11,3 +11,8 @@ services:
ports:
- "127.0.0.1:${THOTH_HTTP_PORT:-8080}:8080"
restart: "no"
workspace-maintenance:
environment:
THT_WORKSPACE_INSTALLATION_ID: local
restart: "no"
+2
View File
@@ -1,3 +1,5 @@
# Retired for operator use: this profile remains only as a non-public engine-fixture path.
# It exercises the legacy preprocessing fixtures and must not become a second operator interface.
services:
preprocess-evidence:
image: thothii-core:local
+15
View File
@@ -35,3 +35,18 @@ services:
ports:
- "${THOTH_SERVER_BIND:-127.0.0.1}:${THOTH_HTTP_PORT:-8080}:8080"
restart: unless-stopped
workspace-maintenance:
environment:
THT_DATA_ROOT: /data
THT_WORKSPACE_INSTALLATION_ID: server
volumes: !override
- type: bind
source: ${THT_DATA_ROOT:?set THT_DATA_ROOT}/sessions
target: /data/sessions
- type: bind
source: ${THT_WORKSPACE_REGISTRY_ROOT:?set THT_WORKSPACE_REGISTRY_ROOT}
target: /data/workspace-registry
read_only: true
restart: "no"
+12 -6
View File
@@ -33,10 +33,16 @@ LABEL org.opencontainers.image.title="thothii-core" \
io.thothii.pi.version="${PI_VERSION}"
# Runtime tools
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates ripgrep fd-find tini git openssh-client \
&& rm -rf /var/lib/apt/lists/* \
&& ln -s /usr/bin/fdfind /usr/local/bin/fd
RUN set -eux; \
runtime_packages="curl ca-certificates ripgrep fd-find tini git openssh-client"; \
if ! command -v flock >/dev/null 2>&1; then \
runtime_packages="$runtime_packages util-linux"; \
fi; \
apt-get update; \
apt-get install -y --no-install-recommends $runtime_packages; \
rm -rf /var/lib/apt/lists/*; \
command -v flock >/dev/null 2>&1; \
ln -s /usr/bin/fdfind /usr/local/bin/fd
# Node 22 + npm copiati dall'immagine ufficiale (stesso Debian bookworm → binario compatibile)
COPY --from=node-runtime /usr/local/bin/node /usr/local/bin/node
@@ -91,10 +97,10 @@ ENV PATH="/opt/venv/bin:/usr/local/bin:$PATH" \
HOME=/home/thoth
COPY scripts/verify-line-endings.sh /usr/local/bin/verify-line-endings
COPY docker/core-entrypoint.sh docker/session-migrate.sh docker/ensure-pi-trust.mjs docker/embedding-model-init.sh /app/docker/
COPY docker/core-entrypoint.sh docker/workspace-maintenance-entrypoint.sh docker/session-migrate.sh docker/ensure-pi-trust.mjs docker/embedding-model-init.sh /app/docker/
COPY docker/smoke/core-smoke.sh /app/docker/smoke/core-smoke.sh
RUN /usr/local/bin/verify-line-endings /app/docker \
&& chmod +x /app/docker/core-entrypoint.sh /app/docker/session-migrate.sh /app/docker/embedding-model-init.sh /app/docker/smoke/core-smoke.sh
&& chmod +x /app/docker/core-entrypoint.sh /app/docker/workspace-maintenance-entrypoint.sh /app/docker/session-migrate.sh /app/docker/embedding-model-init.sh /app/docker/smoke/core-smoke.sh
WORKDIR /app/backend
USER thoth
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
exec node /app/backend/dist/workspace-maintenance.js "$@"
+62 -13
View File
@@ -3,7 +3,10 @@
set -euo pipefail
usage() {
echo "usage: $0 --bindings-env <workspace-bindings.env> --operator-env <operator.env> --output <override.yaml>" >&2
cat >&2 <<'EOF'
usage: $0 --bindings-env <workspace-bindings.env> --operator-env <operator.env> --output <override.yaml> \
[--service <core|workspace-maintenance>]... [--role <all|dwh|evidence>]...
EOF
exit 2
}
@@ -40,20 +43,59 @@ read_env_value() {
printf '%s' "$result"
}
binding_matches_roles() {
local name="$1" role
for role in "${roles[@]}"; do
case "$role" in
all) return 0 ;;
dwh)
[[ "$name" == *"_DWH_"* ]] && return 0
;;
evidence)
[[ "$name" == *"_EVIDENCE_"* ]] && return 0
;;
*)
echo "unsupported role filter: $role" >&2
exit 2
;;
esac
done
return 1
}
bindings_env=""
operator_env=""
output=""
services=()
roles=()
while (($#)); do
case "$1" in
--bindings-env) bindings_env="${2:-}"; shift 2 ;;
--operator-env) operator_env="${2:-}"; shift 2 ;;
--output) output="${2:-}"; shift 2 ;;
--service) services+=("${2:-}"); shift 2 ;;
--role)
roles+=("$(printf '%s' "${2:-}" | tr '[:upper:]' '[:lower:]')")
shift 2
;;
*) usage ;;
esac
done
[[ -f "$bindings_env" && -f "$operator_env" && -n "$output" ]] || usage
[[ ! -e "$output" ]] || { echo "refusing to overwrite connector override: $output" >&2; exit 2; }
((${#services[@]})) || services=(core)
((${#roles[@]})) || roles=(all)
for service in "${services[@]}"; do
case "$service" in
core|workspace-maintenance) ;;
*)
echo "unsupported service target: $service" >&2
exit 2
;;
esac
done
names=()
targets=()
@@ -64,6 +106,7 @@ while IFS=$'\t' read -r name target; do
echo "retired semantic secret binding is not supported: ${name%_FILE}_SOURCE" >&2
exit 2
fi
binding_matches_roles "$name" || continue
[[ "$target" =~ ^/run/secrets/[A-Za-z0-9][A-Za-z0-9_.-]*$ && "$target" != *..* ]] || {
echo "invalid connector secret target for $name: $target" >&2
exit 2
@@ -101,20 +144,25 @@ done < <(
' "$bindings_env"
)
((${#names[@]})) || { echo "no THT_WS_*_FILE connector bindings found in $bindings_env" >&2; exit 2; }
((${#names[@]})) || {
echo "no THT_WS_*_FILE connector bindings matched the selected roles in $bindings_env" >&2
exit 2
}
{
printf '%s\n' '# Generated by scripts/generate-connector-secrets-override.sh; keep this file untracked.'
printf '%s\n' \
'services:' \
' core:' \
' env_file:' \
' - path: ${THT_WORKSPACE_BINDINGS_ENV_FILE:?set THT_WORKSPACE_BINDINGS_ENV_FILE}' \
' required: true' \
' secrets:'
for ((index = 0; index < ${#names[@]}; index += 1)); do
printf ' - source: connector_secret_%d\n' "$((index + 1))"
printf ' target: %s\n' "${targets[index]}"
printf '%s\n' 'services:'
for service in "${services[@]}"; do
printf ' %s:\n' "$service"
printf '%s\n' \
' env_file:' \
' - path: ${THT_WORKSPACE_BINDINGS_ENV_FILE:?set THT_WORKSPACE_BINDINGS_ENV_FILE}' \
' required: true' \
' secrets:'
for ((index = 0; index < ${#names[@]}; index += 1)); do
printf ' - source: connector_secret_%d\n' "$((index + 1))"
printf ' target: %s\n' "${targets[index]}"
done
done
printf '%s\n' '' 'secrets:'
for ((index = 0; index < ${#names[@]}; index += 1)); do
@@ -123,4 +171,5 @@ done < <(
done
} >"$output"
printf 'generated %s connector secret mount(s) at %s\n' "${#names[@]}" "$output"
printf 'generated %s connector secret mount(s) for %s at %s\n' \
"${#names[@]}" "$(IFS=,; printf '%s' "${services[*]}")" "$output"