From 4c51ba422eb3902f1eb459bc156288986db69c89 Mon Sep 17 00:00:00 2001 From: didericis Date: Fri, 8 May 2026 00:59:13 -0400 Subject: [PATCH] feat(manifest): document egress object + add allowlist accessor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the manifest schema doc-comment to include the new bottles..egress.allowlist field added in PRD 0001, and introduces manifest_bottle_egress_allowlist alongside manifest_bottle_ssh — same shape as the existing per-bottle helper, returns one hostname per line, empty for missing field. The accessor performs only top-level array-type validation; per-element string typing happens in lib/pipelock.sh next to the YAML generator that consumes it. Refs: docs/prds/0001-per-agent-egress-proxy-via-pipelock.md Assisted-by: Claude Code --- lib/manifest.sh | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/manifest.sh b/lib/manifest.sh index 6dc496b..d232ad4 100644 --- a/lib/manifest.sh +++ b/lib/manifest.sh @@ -8,7 +8,8 @@ # "bottles": { # "": { # "env": { "": , ... }, -# "ssh": [ , ... ] +# "ssh": [ , ... ], +# "egress": { "allowlist": [ "", ... ] } # }, # ... # }, @@ -22,9 +23,17 @@ # } # } # -# A bottle groups shared infrastructure (SSH keys, known hosts) that multiple -# agents can reference by name. The "bottle" field is required on every agent; -# cli.sh start rejects agents that omit it. +# A bottle groups shared infrastructure (SSH keys, known hosts, egress +# allowlist) that multiple agents can reference by name. The "bottle" field +# is required on every agent; cli.sh start rejects agents that omit it. +# +# The "egress" object is added in PRD 0001. Today it carries one key: +# - allowlist: array of hostnames the agent is allowed to reach. The +# effective allowlist at launch is this list UNIONED with the +# baked-in defaults for Claude Code's required hosts (see +# lib/pipelock.sh). Bottles with no "egress" block use defaults +# only. Future keys (mode, dlp, data_budget, ...) are reserved +# under the same object; v1 ignores anything we don't recognize. # # An is a JSON string. Mode is selected by sentinel prefix: # "?" → prompt for the value at runtime, displaying @@ -225,6 +234,24 @@ manifest_bottle_ssh() { jq -c --arg b "$bottle_name" '.bottles[$b].ssh // [] | .[]' "$manifest_file" } +# manifest_bottle_egress_allowlist — prints one +# hostname per line on stdout for the entries in +# bottles[bottle_name].egress.allowlist. Prints nothing if the field is missing +# or the array is empty. Validates only that the field, when present, is an +# array; per-element string typing is checked at use-time in lib/pipelock.sh +# so the validation lives next to the YAML generator that consumes it. +manifest_bottle_egress_allowlist() { + local manifest_file="${1:?manifest_bottle_egress_allowlist: missing manifest file}" + local bottle_name="${2:?manifest_bottle_egress_allowlist: missing bottle name}" + local field_type + field_type="$(jq -r --arg b "$bottle_name" '.bottles[$b].egress.allowlist | type' "$manifest_file" 2>/dev/null || echo "null")" + case "$field_type" in + array|null) : ;; + *) die "bottle '${bottle_name}' egress.allowlist must be an array (was ${field_type})." ;; + esac + jq -r --arg b "$bottle_name" '.bottles[$b].egress.allowlist // [] | .[]' "$manifest_file" +} + # manifest_ssh — prints one compact JSON object per line # for each ssh entry associated with the agent. SSH entries are resolved via # the agent's "bottle" field: if set, entries come from bottles[bottle].ssh; if the