VAL 06 — Support Bundle Validation¶
1. Purpose and Claims¶
This validation proves that the autonomy support-bundle generate command
produces complete, correctly redacted archives under both normal and degraded
conditions.
# |
Claim |
|---|---|
VAL06-C1 |
Bundle generation succeeds (exit 0, non-empty archive) within 30 seconds under normal lab conditions |
VAL06-C2 |
The archive contains the three always-present core files ( |
VAL06-C3 |
Secrets are redacted: |
VAL06-C4 |
The bundle degrades gracefully: when an optional collector ( |
What “validation” covers here:
VAL06 validates the CLI surface and archive structure against the four claims
above. It does not test bundle ingestion by a support ticketing system, bundle
decryption, or multi-bundle diffing. Per-field value correctness within
system_info.json (exact hostname, CPU count, etc.) is not asserted — only
field presence.
2. Scope¶
Covered¶
Archive creation: exit code, file non-empty, timing ≤ 30 s
Core file presence:
manifest.json,system_info.json,build_info.jsonCollector inventory: all 6 expected collector names present in manifest
system_info.jsonfield presence:os,arch,go_version,hostname,collected_ataudit_recent.jsonpopulation: ≥ 1 audit record from the retained storeSecret redaction:
fleet_saltplaceholder, postgres password placeholderArchive purity: no PEM block (
-----BEGIN) in any archive entryDegraded mode:
ha_statuscollector failure with non-fatal bundle exit
Not covered (known gaps)¶
ha_status content validation: the JSON structure of
ha_status.jsonis validated only by the HA surface tests; VAL06 treats it as opaquelogs collector content: log lines included from
--log-fileare not parsed; VAL06 confirms presence in the archive via manifest onlyConcurrent bundle generation: only one bundle is generated at a time
Archive size: no upper bound is enforced on archive size in this validation
RBAC guard:
support-bundle generateis intentionally unguarded (VAL03 confirms this); VAL06 bypasses RBAC withAUTONOMY_RBAC_ENFORCEMENT=0config_redacted.yaml value correctness: only the two known secret fields are checked; all other YAML values are preserved as-is and not validated here
3. Bundle Architecture¶
autonomy support-bundle generate collects data from up to 6 collectors and
writes a single .tar.gz archive with this layout:
<bundle-dir>/
manifest.json — always written last; records all 6 collectors and their status
system_info.json — os, arch, go_version, num_cpu, hostname, collected_at
build_info.json — cli_version, go_version, main_path, build_settings
config_redacted.yaml — effective config with fleet_salt and postgres_url password replaced
ha_status.json — GET /v1/ha/status snapshot (requires --orchestrator-url)
audit_recent.json — 50 most recent audit records (requires --audit-dir)
logs/
autonomy.log — tailed log lines (requires --log-file)
<bundle-dir> is derived from the output filename: val06-bundle.tar.gz →
val06-bundle.
Collector status values:
Status |
Meaning |
|---|---|
|
Collector ran and wrote its file to the archive |
|
Collector ran but returned an error; file is not in the archive |
|
Required flag ( |
Secret redaction (line-oriented, no full YAML parse required):
Field |
Replacement |
|---|---|
|
|
|
password component replaced with |
Certificate file paths |
preserved as-is (paths are not secrets; contents not collected) |
Errors from individual collectors are non-fatal: the archive is always
written with all available data, and manifest.json records the error message
for each failed collector.
4. Harness¶
VAL06 is implemented as run_support_bundle_val06_lab() in
scripts/labs/run_cli_audit_lab.sh. It runs after run_otel_val05_lab
so the retained audit store already contains records from all prior phases
(needed for the audit_recent_populated check).
Setup performed by the function:
Creates
$WORK_DIR/val06/autonomy.yamlwith a knownfleet_saltvalue (deadbeef…) and a known postgres password (val06-secret-pass) for deterministic redaction verification.Generates a normal bundle (
val06-bundle.tar.gz) with--config-file,--audit-dir, and--log-filebut without--orchestrator-url, while explicitly clearingAUTONOMY_ORCHESTRATOR_URL, soha_statusis skipped (not failed) in the normal run.Extracts
manifest.json,system_info.json,config_redacted.yaml, andaudit_recent.jsonfrom the archive for content inspection.Generates a degraded bundle (
val06-bundle-degraded.tar.gz) with--orchestrator-url http://127.0.0.1:19999(no server listening) to exercise the graceful-failure path.
Evidence directory: $EVIDENCE_DIR/val06/
5. Exact Scenarios¶
VAL06-01 — Archive Created¶
Purpose: Confirm that support-bundle generate exits 0 and writes a
non-empty .tar.gz file.
Action:
AUTONOMY_RBAC_ENFORCEMENT=0 \
autonomy support-bundle generate \
--output val06-bundle.tar.gz \
--config-file autonomy.yaml \
--audit-dir "$AUTONOMY_AUDIT_DIR" \
--log-file server-rbac-enforced.log \
--log-lines 50
Evidence file: val06/val06-timing.txt
Pass criterion: Command exits 0 and val06-bundle.tar.gz is non-empty.
VAL06-02 — Generation Time ≤ 30 s¶
Purpose: Confirm that bundle generation completes within a practical wall- clock bound at lab corpus sizes.
Action: date +%s%3N brackets the generate invocation from VAL06-01.
Evidence file: val06/val06-timing.txt — generate_ok, elapsed_s,
bound_s=30, pass_timing
Pass criterion: elapsed_s ≤ 30 and generate_ok=true.
VAL06-03 — Core Files Present in Archive¶
Purpose: Confirm that manifest.json, system_info.json, and
build_info.json appear in the archive listing for every bundle. These three
files are always collected; their absence indicates a fatal archive-write
failure.
Action:
tar -tzf val06-bundle.tar.gz | grep -c '<filename>'
Evidence file: val06/val06-core-files.txt — PRESENT/ABSENT per file
Pass criterion: All three files report PRESENT.
VAL06-04 — Manifest Records All 6 Collectors¶
Purpose: Confirm that manifest.json contains entries for all 6 collector
names, regardless of their status value. A missing entry means the collector
was silently dropped rather than recorded as skipped or failed.
Expected collector names: system_info, build_info, config,
ha_status, audit_recent, logs
Action: For each collector name, test:
jq -e --arg n "<name>" '.collectors[] | select(.name == $n)' manifest.json
Evidence file: val06/val06-manifest-check.txt — collector=<name> status=<status> lines
Pass criterion: All 6 collector names are present in manifest.json.
VAL06-05 — system_info.json Has Required Fields¶
Purpose: Confirm that the system information collector populates all 5 required fields.
Required fields: os, arch, go_version, hostname, collected_at
Action: For each field:
jq -e --arg f "<field>" 'has($f)' system_info.json
Evidence file: val06/val06-sysinfo-check.txt — PRESENT/ABSENT per field
Pass criterion: All 5 fields are present.
VAL06-06 — audit_recent.json Is Populated¶
Purpose: Confirm that the audit_recent collector returns at least one
record from the retained audit store, proving end-to-end connectivity between
bundle generation and the file-backed audit path.
Action:
jq 'length' audit_recent.json
Evidence file: val06/val06-audit-check.txt — audit_recent_count, pass
Pass criterion: audit_recent_count > 0.
VAL06-07 — fleet_salt Is Redacted¶
Purpose: Confirm that the known fleet_salt value written into the test
config (deadbeef…) is replaced with <REDACTED> in config_redacted.yaml,
and that the original value is absent.
Action:
grep 'fleet_salt: <REDACTED>' config_redacted.yaml # must match
grep 'deadbeef' config_redacted.yaml # must not match
Evidence file: val06/val06-redaction-salt.txt — fleet_salt_placeholder,
fleet_salt_actual_absent, pass
Pass criterion: fleet_salt: <REDACTED> present AND deadbeef absent.
VAL06-08 — Postgres Password Is Redacted¶
Purpose: Confirm that the known postgres password (val06-secret-pass)
embedded in the test config URL is replaced with REDACTED in
config_redacted.yaml, and that the original password is absent.
Action:
grep 'REDACTED' config_redacted.yaml # must match
grep 'val06-secret-pass' config_redacted.yaml # must not match
Evidence file: val06/val06-redaction-pg.txt — pg_redacted_present,
pg_secret_absent, pass
Pass criterion: REDACTED present AND val06-secret-pass absent.
VAL06-09 — Archive Contains No Private Key Material¶
Purpose: Confirm that no PEM block (-----BEGIN …) appears anywhere in the
extracted archive contents. The bundle collects only JSON, YAML, and plain-text
log data; certificate paths in the config are preserved as path strings, not
file contents.
Action: Extract archive to a temp directory and search all files:
grep -r -- '-----BEGIN' <extract-dir>
Evidence file: val06/val06-privkey-check.txt — privkey_hits, pass
Pass criterion: privkey_hits = 0.
VAL06-10 — Degraded Mode Exits 0¶
Purpose: Confirm that the bundle command exits 0 even when the ha_status
collector fails to contact the control plane, and that manifest.json
accurately records the failure.
Action:
AUTONOMY_RBAC_ENFORCEMENT=0 \
autonomy support-bundle generate \
--output val06-bundle-degraded.tar.gz \
--config-file autonomy.yaml \
--audit-dir "$AUTONOMY_AUDIT_DIR" \
--orchestrator-url http://127.0.0.1:19999
Port 19999 has no server listening, so collectHTTPSnapshot returns an
error. The collect() wrapper marks the collector as "failed" and continues.
Evidence file: val06/val06-degraded-check.txt — bundle_exit_ok,
ha_status_status, pass
Pass criterion: bundle_exit_ok=true AND ha_status_status=failed.
6. Evidence Files¶
All files are written to $EVIDENCE_DIR/val06/.
File |
Produced by |
Contains |
|---|---|---|
|
|
Full archive under inspection |
|
normal generate stdout |
Reserved; normally empty because generate progress is emitted on stderr |
|
normal generate stderr |
|
|
|
|
|
|
Archive file listing |
|
|
Full manifest with collector statuses |
|
|
OS/runtime fields |
|
|
Redacted YAML |
|
|
50 most recent audit records |
|
core file loop |
|
|
collector loop |
|
|
field loop |
|
|
|
|
|
grep checks |
|
|
grep checks |
|
|
|
|
|
|
Degraded archive |
|
degraded generate stdout |
Reserved; normally empty because generate progress is emitted on stderr |
|
degraded generate stderr |
|
|
|
Manifest with |
|
degraded mode checks |
|
|
composite report |
10-check PASS/FAIL + summary line |
|
composite report |
Machine-readable JSON with all check statuses |
7. Pass/Fail Criteria¶
Check ID |
Name |
File |
Pass condition |
|---|---|---|---|
VAL06-01 |
archive_created |
|
|
VAL06-02 |
generation_time |
|
|
VAL06-03 |
core_files |
|
All 3 required files report |
VAL06-04 |
manifest_collectors |
|
All 6 collector names present in manifest |
VAL06-05 |
system_info_fields |
|
All 5 required fields report |
VAL06-06 |
audit_recent_populated |
|
|
VAL06-07 |
fleet_salt_redacted |
|
Placeholder present AND original value absent |
VAL06-08 |
postgres_pw_redacted |
|
|
VAL06-09 |
no_private_keys |
|
|
VAL06-10 |
degraded_mode |
|
|
Overall pass: all 10 checks pass and val06-report.txt reports
pass=10 fail=0 total=10.
Failure handling:
VAL06-01 fails: inspect
val06-generate.logfor the generation error; the most common causes are a missing audit dir or write permission issue on the output pathVAL06-02 fails: the generation time exceeded 30 s; check whether the audit store has grown unusually large or the
--audit-dirpath is on a slow filesystemVAL06-03 fails: a required file is absent from the archive; inspect
val06-manifest.jsonto determine which collector failed at the archive- write level (distinct from a collector-level failure recorded asstatus: "failed")VAL06-04 fails: a collector name is absent from
manifest.json; this indicates a code-level removal of a collector; cross-referencesupport_bundle.goto confirmVAL06-05 fails: a required
system_infofield is absent; inspectcollectSystemInfo()insupport_bundle.goVAL06-06 fails: the
audit_recentcollector returned an empty array; check that prior lab phases populated$AUTONOMY_AUDIT_DIRbefore VAL06 ranVAL06-07 or VAL06-08 fails (redaction): inspect
val06-config-redacted.yamldirectly and cross-referenceredactConfigBytes()andredactPostgresURL()insupport_bundle.go; a failure here means a secret was included in plain text in the bundleVAL06-09 fails (
privkey_hits> 0): inspect which file contains-----BEGINby re-runninggrep -r -- '-----BEGIN'on the extracted archive; a code change may have accidentally included PEM material such as a cert or key file’s contentsVAL06-10 fails: either the bundle exited non-zero when
ha_statusfailed (regression in the non-fatal collector pattern), orha_statusdid not appear as"failed"in the manifest; inspectval06-degraded.logandval06-degraded-manifest.json
8. Report Template¶
# VAL 06 — Support Bundle Validation Report
timestamp: 2026-03-20T10:00:00Z
## Results
VAL06-01 archive_created: PASS
VAL06-02 generation_time: PASS (elapsed=0s bound=30s)
VAL06-03 core_files: PASS
VAL06-04 manifest_collectors: PASS
VAL06-05 system_info_fields: PASS
VAL06-06 audit_recent_populated: PASS
VAL06-07 fleet_salt_redacted: PASS
VAL06-08 postgres_pw_redacted: PASS
VAL06-09 no_private_keys: PASS
VAL06-10 degraded_mode: PASS
## Summary
pass=10 fail=0 total=10
The runner also prints
VAL 06: pass=10 fail=0 total=10 (report: val06-report.txt) to stdout so CI
log scanners can grep for VAL 06: pass= without parsing the report file.
9. How to Run¶
VAL06 executes automatically as the final validation slice when the full lab is run:
export GOROOT=/home/ubuntu/.local/go1.25.7
export PATH="$GOROOT/bin:$PATH"
export GOTOOLCHAIN=local
bash scripts/labs/run_cli_audit_lab.sh
To inspect results after a run:
# Quick pass/fail
cat evidence/pr17-cli-audit-local-2026-03-17/val06/val06-report.txt
# Collector status breakdown
cat evidence/pr17-cli-audit-local-2026-03-17/val06/val06-manifest-check.txt
# Redaction verification
cat evidence/pr17-cli-audit-local-2026-03-17/val06/val06-redaction-salt.txt
cat evidence/pr17-cli-audit-local-2026-03-17/val06/val06-redaction-pg.txt
# system_info field presence
cat evidence/pr17-cli-audit-local-2026-03-17/val06/val06-sysinfo-check.txt
# Degraded mode result
cat evidence/pr17-cli-audit-local-2026-03-17/val06/val06-degraded-check.txt
# Machine-readable report
jq '{pass_count, fail_count, elapsed_s}' \
evidence/pr17-cli-audit-local-2026-03-17/val06/val06-report.json
# Inspect the archive listing
cat evidence/pr17-cli-audit-local-2026-03-17/val06/val06-contents.txt
# Check one extracted field
jq '{os, arch, go_version, hostname}' \
evidence/pr17-cli-audit-local-2026-03-17/val06/val06-system-info.json