Komodo deployment uplift — 2026-10-25¶
TL;DR¶
All high/medium audit findings have source implementations or explicit operating
policies. The platform remains Komodo/file-on-host, with secret-safe input
selection, exact execution verification and independent host chains. PR #506 is
merged; production bootstrap is complete and workload rollout remains pending. The first
deployment attempt exposed a parent-directory permission conflict.
PR #508 is merged, and the owner-approved credential migration is complete.
The existing encrypted API pair is unchanged. PR #510 merged as 0416d8e with
all 26 checks passed, and the approved LAN runner activation is now applied.
That activation used /opt/homelab at 0416d8e, preserving #509 and unrelated work;
the later bootstrap preflight found 8bf7aa5 (#511), then an independent writer
advanced the host to 0f522f0 before the input-preparation writes.
Runner identity, read-only directory access and non-admin authentication passed.
At 2026-10-27 10:49:47 UTC, exact-run quarantine safely replaced the historical
broad LAN drain. The service and actual container are running with the guard retained;
GitHub verifies homelab-lan-01 online and non-deploying qualification successful;
normal LAN job execution is restored. The edge orphan is
still queued with zero jobs. Both Ansible timers are active, with no maintenance,
drain or relay pending intent. Actual-runner credential/convergence acceptance
is complete, including both naturally occurring shared-parent modes and a full
subsequent apply. All five missing input profiles are now prepared and verified.
Phase: edge_dispatch_blocked_github_queue. Reddit's desired fingerprint was
reconciled using its existing key, all seven input checks passed, and the approved
external Sync and least-privilege grants completed. Deployment stays disabled.
No workload deployment or Core/Periphery recreation was performed by this rollout.
Fleet API operations were subsequently recovered on a general runner while the
separate edge run remains quarantined. The owner merged the docs-only recovery
PR #519 as 4b2cb0c, without source/service
behavior changes. The local-retention source fix
is merged in #520; live convergence and next scheduled-run validation remain pending.
The separately owner-approved exact-snapshot B2 catch-up is complete,
closing only the two offsite snapshot gaps, not restore/retention acceptance.
PR #518's bridge maintenance hold and all rollout restrictions remain.
Exact-run quarantine replaced the broad LAN hold¶
2026-10-27 10:49:47 UTC: installed and tested job admission for orphan edge run
40469274207, then removed only the owned
/var/lib/github-runner/drain/homelab-lan-01 marker, backed up root-private.
The broad drain/offline containment below is historical, not the current runner state.
komodo-deploy.yml stays disabled_manually; the orphan remains queued with zero
jobs. No redispatch, cancel/rerun retry or deployment occurred in this restoration.
- Guard
/etc/komodo-deploy/job-admission.sh:root:1001 0750under the existingroot:1001 0710directory, using its existing read-only bind mount. SHA-256:a30f9450558ef734478141a6e9c459bc9af709a3b65de2f12e0e78275b62f632. - Instance environment:
root:root 0600, with the unquoted literal entryACTIONS_RUNNER_HOOK_JOB_STARTED=/etc/komodo-deploy/job-admission.sh. Its original is privately backed up and contains a PAT; never print either environment copy. - Verifier
/etc/komodo-deploy/verify-job-admission.py:root:root 0600, called by/etc/systemd/system/github-runner@homelab-lan-01.service.d/90-orphan-run-guard.confusingExecStartPre=/usr/bin/python3 -I -B /etc/komodo-deploy/verify-job-admission.py. It fails closed on changed hash, modes, mount/launcher contract or lost hook config. Explicit runner-role apply may rewrite the environment; normal Ansible site does not apply that role. Preserve/revalidate the guard rather than bypassing the verifier. - Real cached runner-image tests, network none, default 1001:1001, passed: orphan exit 1, another numeric homelab run exit 0, missing identity exit 2. Invalid/non-homelab identity is rejected too. Verifier positive and changed-hash rejection tests passed before release of the owned drain marker.
- Actual LAN container is running as 1001:1001 plus group 10002, with correct hook,
read-only mount and no Docker socket; service is active and GitHub verifies
homelab-lan-01online. - Single non-deploying qualification run 40491873440,
main
54ca4da: validation placed onhomelab-ci-02, matrix onhomelab-mgmt-01, and exacthomelab-lan-01job 122148472245 completed SUCCESS, as did the run, verified via GitHub API. Set up runner (pre-job hook), checkout and Run Linux and macOS qualification checks succeeded. Normal LAN job execution is restored; no new test run was needed. - Both
ansible-pull-check.timerandansible-pull-apply.timerare active;/etc/homelab/MAINTENANCE, relaypending.jsonand the owned drain marker are absent. Privatecutover-state.jsonandedge-window-20261027.jsonrecord guarded resume and qualification run40491873440; main phase remainsedge_dispatch_blocked_github_queue. Temporary exact-homelab-ci-02API routing, management/deploy selectors and watchdog's guarded mutation contract are unchanged.
Do not remove the guard because the runner is restored. Removal requires proof that the orphan is terminal with no executable pending work, plus deliberate reviewed follow-up. No automatic edge redispatch, cancel/rerun retry, workflow enablement or UI deployment bypass. The canonical guard contract preserves the edge quarantine despite successful registration and qualification.
Current health and limitations: 2026-10-27¶
The 10:53:25 UTC live snapshot found 49 running containers including the restored runner: 38 healthy, 11 without healthchecks, zero unhealthy or restarting. Earlier DNS/TLS smoke checks were good. This is runtime/route evidence, not authenticated application or data acceptance, and does not accept the blocked edge deployment.
- OneUptime and adapter October 27 B2 gaps are closed by explicitly owner-approved
exact-snapshot copies; see the completed catch-up receipt.
The local-retention fix
is merged in #520, but affected live units still lacked
--retry-lock=5mat the preflight. Normal convergence and next scheduled-run validation remain pending; this is not restore/retention acceptance or broader mutation permission. - The image check safely failed closed at 13/14 stacks, 32 services. Rexit's
latest service set is
exporter+studio, versus deployedstudio; native checking prefersdeployed_services. This is an inventory/accounting mismatch, not anauto_pullissue; do not weaken accounting, flip pull policy or deploy merely to make the report green. - Gmail's Docker variable is absent and the proposed Docker runner is unregistered:
separate provisioning work, not caused by this recovery. Python/Maintenance job
successes do not establish Docker capacity. Separate Gmail maintenance
run 40489590509 placed
validation and preview successfully, but management-runner apply failed SSH with
No route to hostfor172.29.240.13/gha-docker-a, while inspecting the drain marker forgmail-order-tracker-docker-01. Operationdrain,changed=0: no mutation executed. An offline/unreachable on-demand host is plausible; sleeping is not an established cause. No blind replay or waking unrelated VMs. - The historical aiProject August 19 ghost predates and was not caused by this work.
The user may resume normal work within these limits. No mass restarts, automatic edge redispatch or replay of unknown canceled work; use intended scope and dry-run for any separately reviewed recovery. Guard removal and deployment remain separate reviewed follow-ups; successful LAN qualification does not authorize deployment.
Backup verification: 2026-10-27¶
Historical missing-copy evidence: read-only verification completed 11:29–11:30 UTC; all times below are UTC. Both main backups succeeded. All B2 snapshot metadata was scanned: 28 OneUptime / 12 adapter snapshots. Neither October 27 snapshot was found by originating ID or matching tree/time/path/host; both offsite copies were verified missing at that capture. The later approved catch-up closed both gaps. This establishes snapshot metadata presence/absence, not restore integrity.
| Backup | Main snapshot | Saved October 27 | Latest B2 snapshot at 11:29–11:30 |
|---|---|---|---|
| OneUptime | 4751b022 |
09:00:43 | October 26 09:00:32 |
| Incident adapter | 5dd10199 |
09:02:55 | October 26 09:01:45 |
Both failures were local restic forget --prune, before B2 copy, exit 11:
OneUptime at 09:00:45, shared lock d9ceb188, PID 3763461; adapter at
09:02:56, shared lock 447634a2, PID 3772442. Fresh shared-lock contention
is confirmed; exact process identity is unknown. Metrics collection overlaps the
first failure and takes read locks: a plausible, not proven, cause.
At 11:30:13, both old PIDs were gone and local lock directories were empty; the active metrics reader was left untouched. No unlock, prune, copy, backup, restart or reset was performed during verification.
At that verification, --retry-lock=5m on local retention was proposed only;
no mitigation or catch-up copies were applied. Subsequent source preparation on
fix/restic-retention-lock-retry added the five-minute lock-acquisition retry to
the shared template's first, local-retention post-step; it ran no copy, prune,
backup, live apply or restart. Catch-up copies then required separate explicit
authorization, subsequently granted and completed only for these two snapshots
as recorded below.
B2 catch-up complete: 2026-10-27¶
Explicitly owner-approved exact-snapshot catch-up succeeded, completed
11:55:41.471 UTC. Preflight at 11:55:03 UTC confirmed both copies were still
missing. Only the two fully resolved source snapshot IDs below were copied,
individually, into their existing B2 repositories with normal locking and
--no-cache --retry-lock=1m on the copy commands, not as a live unit alteration.
| Backup | Exact source snapshot ID | Verified B2 destination snapshot ID | Verified UTC |
|---|---|---|---|
| OneUptime | 4751b022d9d0ab59d875d440a52d74b752178c92cd784738b52d418b9d3fcb57 |
9fb98052adba53c9a5828e5e789179cabe58e7125421795a0e5a5670b11b0006 |
11:55:29 |
| Incident adapter | 5dd10199596eaeb8f47589cb8705432412c698d72f1710a8dd2fb6bb1a26ca3d |
288782529f85afa345dea1e6ffea3b4f415051539d2a1f9324cdfa56d2fc8b02 |
11:55:41 |
Both destinations matched the exact originating snapshot ID (original) and corroborating
tree, time, paths and host. The root-owned, mode 0600 private result receipt is
/etc/homelab/komodo-deploy-bootstrap-20261026/b2-catch-up-result-20261027.json,
with the plan alongside. This closes only these two offsite snapshot gaps:
it does not establish restore integrity, successful retention or future scheduled-run
acceptance. Original failed-unit evidence was deliberately left untouched.
Post-copy systemctl inspection confirmed both backup units remain failed
(Result=exit-code, ExecMainStatus=0); both ansible-pull-check.timer and
ansible-pull-apply.timer remain active. No unlock, prune, backup, reset,
restart or Ansible apply was performed in this catch-up.
The scoped approval is completed, not general permission for further mutations.
PR #520 is confirmed merged as
ea4acc863e4d2b3f963b8f4a34c2f90a66809a30. Its shared-template local-retention
--retry-lock=5m bounds lock acquisition, not total retention runtime, for all
managed jobs once normal Ansible convergence renders the units. At the 11:55:03
preflight, the affected live units still lacked that flag. Merged source is not
live convergence or next scheduled-run acceptance; both remain pending. See the
canonical source-fix contract.
Normal timers/convergence remain on; no forced apply. Edge quarantine, disabled
deployment and #518's bridge hold are unchanged; Gmail and Rexit remain separate
exceptions.
Fleet API routing recovered with edge run quarantined¶
The owner's report that work was no longer placing exposed collateral from the LAN
containment: fleet dispatch validation also used homelab-lan-01. Run
40472345328 queued at 2026-10-27 08:16 UTC on LAN/deploy labels after the
08:14 hold, then was cancelled at 09:31 with no runner/steps and all downstream
routes skipped. The hold blocked that entry point; it did not stop the general,
Docker or management fleet. Earlier fleet operations applied only a Gmail CI resume
(run 40469102836) and previewed a Gmail Docker drain (40471032755, dry-run only).
Independent evidence distinguished placement from workload failures: homelab's 09:15 security workflow placed jobs on CI runners at 09:17, and Gmail's subsequent Python/maintenance jobs completed on its CI runner. Idle Hyper-V guests normally sleep after 900 seconds, so an all-offline snapshot alone is not a fleet outage. GitHub's public status reported operational at inspection; the individual edge orphan still remains unresolved.
At 10:03:15 UTC, changed only repository variable RUNNER_FLEET_API_RUNS_ON:
- Before:
["self-hosted","Linux","X64","homelab","lan","deploy"] - After:
["self-hosted","Linux","X64","homelab","ci","homelab-ci-02"]
This uses an existing exact general instance, avoiding Docker/LAN routing. It covers
API-only validation, observation/planning, scheduled checks and guarded watchdog
operations; it does not mean every possible operation is read-only. Existing
watchdog authorization/environment guards remain unchanged. DEPLOY_RUNS_ON stayed
LAN-only, and RUNNER_FLEET_MAINTENANCE_RUNS_ON stayed on runner-management.
No host/VM restart, label, credential, permission or maintenance apply was performed.
Verification was one read-only homelab health dispatch,
run 40485555074,
dry_run=true, authorize_disruptive=false, on main 54ca4da. It completed
successfully at 10:04:43 UTC: validation took 17 seconds and observation 22
seconds, both actually on homelab-ci-02; other routes were skipped. Its report
showed 7 online / 2 offline, 9 discovered / 10 expected and 3 drifts. This proves
fleet API routing works again, not that every health finding or unrelated CI failure
is fixed. No broad test campaign or fleet-wide restart was used.
The previous selector and proof run are recorded in local private artifact
artifacts/cutover-20261026/fleet-api-routing-recovery-20261027.json. The recovery
selector is temporary and single-instance, dependent on Hyper-V wake/registration;
it does not establish always-on redundancy. At that capture, removing it would
have restored the held-LAN fallback. Keep it until deliberate routing reassessment;
the later guarded LAN restoration does not automatically revert this override. See the
canonical recovery procedure.
At the 10:03 UTC recovery capture, original edge run 40469274207 had no jobs,
workflow was disabled, LAN runner drained/offline, and normal Ansible timers active.
The 10:49:47 UTC exact-run guard above supersedes that broad hold, not the API
routing override. Fleet operations can be requested again, but an unknown cancelled
mutation is not replayed automatically and no new edge dispatch is authorized.
Current phase remains edge_dispatch_blocked_github_queue; the authorized docs-only
recovery PR does not release the orphan or authorize deployment.
Edge dispatch blocked before job creation¶
After merging #517, the owner authorized only the edge cohort through the existing
workflow. Main 35f9a67d218f78251cedb2dd26a1e1c9edfceee1 had all 27 PR checks
passed. Compared with the host's 0f522f0, edge definitions/pins and deployment
code were unchanged. The exact live Procedure was PullRepo homelab followed by
conditional traefik,homepage,adguard; all managed resources were idle.
The normal Ansible apply finished before an owned window paused the two pull timers.
The workflow was briefly enabled and one confirmed main dispatch accepted at
2026-10-27 07:47:46 UTC as
run 40469274207,
attempt 1, target deploy-reviewed-images-edge. Intake was immediately disabled
again. The bounded 1,230-second watch expired with the run still queued,
zero jobs and zero check runs, while the eligible runner was online/not busy.
Re-enabling the workflow and a bounded job-creation check did not advance it.
Normal cancellation and force-cancellation both returned HTTP 409, saying
Cannot cancel a workflow run that has not been queued yet. An in-place rerun
request was refused with HTTP 403, This workflow is already running.
No cancellation or retry was accepted; no second dispatch was issued. Immediately
closing intake may have contributed, but the cause is not established. Do not
claim an edge deployment failure, success or conditional no-op when no job started.
Verified: host HEAD remained 0f522f0, no relay pending intent existed, no edge
RunProcedure Update was created (its only Update was the earlier CreateProcedure),
and the last Sync remained the successful bootstrap at 824c148. Traefik, Homepage,
AdGuard, Unbound and Core/Periphery container IDs/start times were unchanged.
Historical broad hold — 08:14 UTC, superseded at 10:49:47 UTC: to prevent a
latent queued run starting outside the window, the idle LAN runner
was stopped after checking GitHub busy=false and no Runner.Worker. Its owned
/var/lib/github-runner/drain/homelab-lan-01 marker records run 40469274207;
no job was interrupted. The service was stopped/failed with no container, and GitHub
reported offline. Workflow intake was disabled and remains so. Both Ansible timers were restored
active and only the owned /etc/homelab/MAINTENANCE marker was removed; routine
convergence is not stranded while awaiting GitHub recovery.
Private edge-window-20261027.json and cutover state record
edge_dispatch_blocked_github_queue. At that capture the instruction was to
confirm terminal state and reconcile before retry or runner resume; the later
exact-run guard permits guarded runner restoration without accepting the orphan.
The connected browser could not view this private GitHub page, although authenticated
REST access worked. The then-proposed UI inspection/cancellation was unverified.
Current guidance is inspection/support reconciliation without further cancel/rerun
retries, workflow enablement or direct Procedure execution. Keep the guard until
terminal/no-executable-pending-work proof and deliberate reviewed removal; do not
redispatch automatically.
The qualified runner configuration, completed bootstrap, 26 Procedures / 2 Actions and 51 Read + 5 Execute grants remain intact. Only the edge execution/acceptance is blocked. This checkpoint is included in the authorized docs-only recovery PR, without production source changes; record an accepted run only after verified recovery.
Bootstrap and least-privilege grants complete¶
The owner approved the coordinated bootstrap package and asked for focused checks rather than another broad test campaign. Completed on 2026-10-27:
- Rechecked the reviewed pending source at
824c148, byte-identical to the approved resource SHA-2562551c36fe2b846ab5123e0c52f1a6823cc36f2c6ac3bbeaa2b84c0ca8e8b1c50. Scope remained 18 creations + 13 updates, with no deletes, deployment targets, variable/user-group changes or errors. Existing resources/Sync were idle. - Opened an owned maintenance window and paused only pull timers. Held pull and
relay locks. Refreshed only Reddit's desired fingerprint, using the existing
key and current reviewed #514 inputs. Its key and rendered environment were
byte/metadata-unchanged; no secret render or key rotation occurred. All seven
native input
--checkcalls passed. - Issued one explicit external
RunSyncusing the existing scoped deployment credential—not the general workflow or a deployment Procedure. The persistent execution journal recorded the exact ID before polling. Update6ae04f5aa10ff54a5801dc65completedComplete,success=true; operation acceptance and post-Sync verification ran 07:14:17–07:14:52 UTC.last_sync_hash=824c148, with zero pending resource changes/deployments. - Through the existing administrator browser session and supported native permission
API, added exactly 18 per-resource grants: 15 Read + 3 Execute. The original
38 grants were unchanged. Independent service-user readback at 07:16:31 UTC
verified all 56 effective grants = 51 Read + 5 Execute.
homelab-deployremains enabled, non-admin/non-super-admin, with empty base/specific permissions and no server/build creation rights. No session credential was exported or new key created. - Live inventory is now 26 Procedures, 2 Actions, 6 Repos, 21 Stacks and the
existing Sync. Execute remains only on
infra-services-01,deploy-infra, anddeploy-reviewed-images-{edge,documents,finance}. Every other managed resource is Read-only to this identity; no Action Execute or downstream Write was added. - Verified stored inline Action bodies equal reviewed source, and both Actions have startup/schedule/webhook execution disabled. The four new operator-only retry webhooks are explicitly disabled in live configuration. Existing scoped webhook contracts are unchanged.
The following schedules are now adopted in America/Los_Angeles:
| Procedure | Live schedule |
|---|---|
Backup Core Database |
Enabled, daily 01:00 |
Global Auto Update |
Schedule disabled; retained manual check-only definition |
check-image-updates |
Enabled, daily 03:00 native check-only Action |
Rotate Server Keys |
Enabled, daily 06:00 |
No Procedure/Action test run, workload build/pull/deployment or container restart was
requested. The Sync's normal Core-side source fetch is not a host PullRepo or
on_pull invocation. Core/Periphery retain their original 2026-10-25 11:33 UTC
start times. Host /opt/homelab remains at 0f522f0; #516's runner proposal was
not provisioned by this work. Restored both pull timers to active and removed only
the owned maintenance marker; no drain marker or relay pending intent remains.
The deployment workflow is still disabled.
Private receipts: bootstrap-window-20261027.json,
bootstrap-input-acceptance-20261027.json, bootstrap-sync-result-20261027.json,
and bootstrap-grants-acceptance-20261027.json under the existing root-private
bootstrap directory. The phase is bootstrapped_deployment_pending. Validation
was limited to the actual input/Sync/permission/configuration results and targeted
documentation checks; no broad local regression suite was rerun for this step.
Next is owner review of the operational-record PR and a separately approved first explicit deployment with application acceptance, or optional image-publisher/App and Core-dispatch-token provisioning. The native image-check schedule is configured, not yet execution-qualified by a manual Action run. Optional credentials remain unprovisioned, and manual GitHub registry checks remain available independently.
PR #517 CI wording correction¶
The Python job in run 40466772147
found one documentation-contract failure: the bootstrap record changed the required
phrase base permissions are empty to base permissions remain empty. Restored
that explicit wording without weakening the test or changing the permission policy.
All six focused provisioning/recovery contract tests passed; no broad local
suite or production operation was repeated for this correction.
Five-profile input preparation complete¶
This historical input-preparation checkpoint precedes the completed bootstrap above. The owner approved the input-only window and asked for larger progress. The five scoped profiles were prepared and verified on 2026-10-27, 05:24:58–05:25:28 UTC without building, pulling, restarting workloads, executing Sync or changing grants.
Scope and checkout preservation¶
- Allowed the active normal Ansible apply to finish without interruption. Fresh native API reads found the existing managed baseline and Sync idle, with no nonterminal Updates; GitHub deployment remained disabled and no queued/running repository jobs were present at the preflight.
- Newer main included #514 Reddit gallery-worker library access and #515 Phoenix
routing. The initial checkout plan pinned reviewed
645bc3cto avoid applying unrelated live-watched configuration. Its exact-HEAD guard then detected the host had independently advanced from8bf7aa5to0f522f0and refused before fetch/merge. No rollback or host checkout write was performed by this attempt. - Confirmed the five profiles' rendering code/configuration, shared input engine and
resource file were unchanged between approved
645bc3cand current0f522f0. Used the current checkout rather than overwriting the newer work. Held both pull and relay locks for preparation; all four unrelated tracked edits stayed identical. - Paused only the two pull timers with an owned maintenance marker. The runner was not drained/restarted. Both timers were restored active and the owned marker removed in the controller's cleanup; no relay pending intent was created.
Credential and preparation results¶
A root-private operator checker compared SOPS-derived candidates, resolved canonical Compose configuration and actual running environments in memory, with sanitized results only. All five passed. Orders' OAuth JSON also matched the canonical file and both running containers' pinned read-only file mounts. Firefly PostgreSQL 18 and incident-adapter PostgreSQL 15 passed fresh password-authenticated, read-only TCP sessions, role/expiry checks and first-matching password-HBA checks. HBA files were stable and predated postmaster start; no reload, SQL write or credential rotation was performed. External tokens were compared, not independently validity-tested.
The initial operator checker needed compatibility corrections for the existing
root:docker 0440 age key, valid block-style SOPS recipient formatting and PostgreSQL
inet::text CIDR output. Only the ignored operator artifact was corrected; host key
permissions and encrypted inputs were not changed. Earlier checker failures were
not reported as application failures or successful acceptance.
| Profile | Native render + check | Rendered outputs | New private state |
|---|---|---|---|
oneuptime-incident-adapter |
Passed, check exit 0 | compose.env byte-identical |
Valid |
paperless-stirling-bridge |
Passed, check exit 0 | .env byte-identical |
Valid |
firefly |
Passed, check exit 0 | .env byte-identical |
Valid |
orders |
Passed, check exit 0 | compose.env and OAuth JSON byte-identical |
Valid |
gh-pr-dashboard |
Passed, check exit 0 | compose.env byte-identical |
Valid |
Each new .deploy-inputs directory is 1001:1001 0700, with key/fingerprint
1001:1001 0600. The native serial path used no Docker/nsenter fallback; Docker
was used only for read-only compatibility inspection and existing-container probes.
Private backups of the six pre-existing rendered files remain under the bootstrap
staging directory. Subsequent credential checks passed again. Target applications
remained running/healthy, and their container IDs/start times were unchanged.
Core/Periphery were not recreated.
Existing Reddit baseline remains explicitly failed¶
LiteLLM's read-only check passed. Reddit's check now reported missing or stale fingerprint, despite both private state files being present and its source checkout clean. The overall input audit remained nonzero; it was not relabelled as a global pass. The five independent target profiles passed their own credential/DB gates and were prepared within the authorized write scope. At that checkpoint Reddit's failure still blocked bootstrap; no Reddit/LiteLLM refresh was performed then. Their environment, key and fingerprint bytes and metadata were unchanged.
A read-only HMAC comparison isolated the cause: the stored Reddit fingerprint
exactly matches current application HEAD
3f0b0c9fd6b520fc8c12e3f73b1e0b93aaf42e46 plus the pre-#514 Compose bytes. Keeping
all other input components unchanged reproduces the stored value. Thus the stale
baseline is explained by the reviewed gallery-worker read-only /library Compose
change, not by a secret mismatch or missing key. No key, MAC or input contents were
printed, and no state was written by that diagnostic. A refreshed desired fingerprint
would still not prove deployment/application success.
The phase then was five_inputs_prepared_reddit_baseline_pending, with
bootstrap_ready=false. The proposed next approval bundled: inspect/reconcile
Reddit's desired fingerprint using its existing key, without environment render
or key rotation; verify all inputs; re-review fresh Sync source and the 31-change
preview; execute the external Sync and verify its exact successful Update; then add
only 15 Read + 3 Execute grants (51 Read + 5 Execute total). That bundle was
subsequently approved and completed as recorded above. Workflow enablement, workload deployment and
optional publisher/Core credentials remain excluded.
Private receipts under /etc/homelab/komodo-deploy-bootstrap-20261026/ include
input-preparation-window-645bc3c.json, input-preparation-result-20261027.json,
per-profile logs, before/after credential-check reports and
input-output-backups-20261027/. Retain these alongside prior cutover evidence.
All documentation/agent-guidance surfaces are updated locally for the operational
record; no new source/configuration commit or PR was created in this window.
Retry guard merged: input preparation ready for approval¶
This historical readiness checkpoint precedes the completed five-profile window above.
The owner merged PR #513 at
2026-10-26 20:20:07 UTC as 645bc3c5467f82ef749048ef296b457a48476e39; all
26 PR-head checks passed. The local reviewed checkout is at that merge.
Resource validation and inline Action agreement passed again.
The first native read still showed cached c9a0fd7. Without forcing refresh, Sync's
normal poll advanced its pending preview to 645bc3c at 20:24 UTC. The single
resource file exactly matched merged source (SHA-256
2551c36fe2b846ab5123e0c52f1a6823cc36f2c6ac3bbeaa2b84c0ca8e8b1c50). All four
operator-only retry definitions explicitly had webhook_enabled=false. The preview
still contained 18 creations and 13 updates, no deletions/deployment targets,
errors, variables or user-group changes; managed/delete remained false.
This closes the source-guard merge/re-preview gate, not live resource bootstrap:
none of the new Procedures or Actions was imported. Private evidence is
/etc/homelab/komodo-deploy-bootstrap-20261026/bootstrap-preview-645bc3c.json.
Read-only preparation checks¶
- Native root
Inputs.snapshot(rendered=False)scans passed for all six new profiles. They read source/config/ciphertext but did not decrypt, render, publish fingerprints or invoke a renderer. The Orders and PR Dashboard scans also passed their clean-source requirements, including ignored-file exclusion. - Existing LiteLLM and Reddit fingerprint
--checkcalls both returned 0. Preserve their keys and outputs rather than unnecessarily refreshing them. Fingerprint checks are not proof of live database authentication or credential expiry; Reddit's separate fingerprint also does not establish SOPS/rendered-env equality. Those secret/runtime prerequisites remain distinct. - Native Python was 3.12.3, SOPS was available, and
someoneresolved to1001:1001. Every service directory was1001:1001, mode0755or0750. Five profiles had no state directory/key/fingerprint. Existing output files were single-link regular files, with no symlink ancestors; renderer-owned data/client directories already had the expected metadata. Recheck in the approved window. - Host
/opt/homelabremained8bf7aa5and normal Ansible apply was active; it was not interrupted. No checkout update, timer pause, drain/maintenance marker, environment write, fingerprint refresh, Sync, grant change or restart occurred.
Proposed input-only approval scope¶
Approval had not yet been given at this checkpoint. The proposed sequence was to wait for active convergence to finish and coordinate conflicting writers. Safely advance the host to reviewed merged source under the appropriate locks, preserving unrelated edits and #511. Before output writes, recheck destination safety, source stability and private credential consistency with the actual running consumers; require fresh read-only database authentication where applicable. Stop on mismatch, expiry or failed authentication; this approval must not imply credential rotation, database changes or repair.
Prepare only the five missing profiles, serially via native host Python with a
clean environment—no Docker/nsenter fallback. Each refresh is followed by --check:
| Profile | Approved candidate renderer outputs and metadata |
|---|---|
oneuptime-incident-adapter |
Service compose.env, root:root 0600 under root execution |
paperless-stirling-bridge |
Service .env, root:root 0600 under root execution |
firefly |
Service .env, root:root 0600 under root execution; existing native SOPS backend |
orders |
Service compose.env, 1001:1001 0600; /etc/homelab/orders/google_client.json, 10001:10001 0400 |
gh-pr-dashboard |
Service compose.env, 1001:1001 0600 |
Each may create a service-owned 0700 .deploy-inputs directory, private
0600 key and fingerprint, and private temporary render/state files. Existing
valid HMAC keys must be reused; missing-key/unsafe-state failures do not authorize
resetting state. Orders/dashboard renderers also ensure their existing client/data
directory modes/owners: /etc/homelab/orders root:root 0755, Orders/dashboard
/var/lib/homelab/* directories 10001:10001 0750, and the dashboard gh child
10001:10001 0700. This is not recursive data ownership repair.
Recheck LiteLLM and Reddit read-only; no refresh of their existing outputs/keys is proposed. Stop at the first error and inspect partial effects: a refresh can replace outputs even when fingerprint publication fails or reports unchanged. It is not a transaction or an application-health receipt. No builds, source/image pulls, workload/container restarts, Sync, grants or workflow enablement are included. Input preparation, then bootstrap/grants, then deployment remain separately gated. No owner credential recapture is needed for this proposed scope. Documentation and agent-guidance updates are retained locally for the next operational record.
Bootstrap preflight and operator-only retry guard¶
The owner merged PR #512 at
2026-10-26 19:17:51 UTC as c9a0fd7b199a97bcac25418d3df71d9600753fd7, then asked
for the next step. This historical preflight preceded #513's merge above and
inspected the bootstrap proposal and permissions;
it did not execute Sync, grant permissions, prepare inputs or deploy anything.
Runner qualification remains complete; phase remains runner_qualified_bootstrap_pending.
Reviewed live preview¶
The 19:25 UTC native GetResourceSync response for infra-services-01
(6a0407af4c98985723c7d07b) reported pending revision c9a0fd7. Its single remote
file was services/komodo/resources.toml, byte-identical to reviewed merged source
(SHA-256 a1b2d9971648556eed7e2de92d42b158e782d1596da4bf4501801506aa115be5).
Full resource validation and inline Action agreement passed.
| Preview operation | Exact scope |
|---|---|
| Create 16 Procedures | Six host/application children; four operator-only retries; six image check/proposal/cohort Procedures |
| Create 2 Actions | check-image-updates, request-image-pin-updates |
| Update 6 Stacks | orders, gh-pr-dashboard, oneuptime-incident-adapter, litellm, firefly, paperless-stirling-bridge |
| Update 3 Repos | homelab, orders-source, gh-pr-dashboard-source input-refresh hooks |
| Update 4 Procedures | deploy-infra graph/webhook, plus adoption of Backup Core Database, Global Auto Update, Rotate Server Keys |
Total: 31 changes = 18 creations + 13 updates. There were no deletions,
pending deployment targets, errors, variable changes or user-group changes.
managed=false, delete=false, source path/linked Repo matched expectations, and
Sync was idle. The existing Sync webhook remains enabled; the disabled GitHub
deployment workflow is not a global lock on independent callers.
The two Action data.proposed preview strings are not portable standard TOML:
Python tomllib rejects their generated multiline JavaScript escaping. This is a
preview/export compatibility limitation, not a source parse failure. Upstream
v2.3.4 RunSync refetches and parses source into typed resources, then passes typed
Action configuration to create/update handlers; it does not reparse those cached
preview strings. Do not rewrite Action regexes to repair the display. Conversely,
a preview is not immutable authorization: recheck fresh source and pending
changes immediately before any approved execution.
Stop condition: new operator-only retry webhooks¶
The four new force-retry Procedures omitted webhook_enabled; in Komodo v2.3.4
omission defaults true. That would add webhook-triggerable unconditional
DeployStack paths despite the source calling them operator-only:
deploy-litellmdeploy-oneuptime-incident-adapterdeploy-paperless-stirling-bridgedeploy-firefly
The owner authorized publishing the correction on
fix/komodo-operator-retry-webhooks. PR #513 added explicit
webhook_enabled = false on exactly these four; it did not execute a live Sync.
The existing retry validator
rejects omission or true, with mutation tests for both defects and preservation of
the six legacy scoped release/retry/canary webhook contracts. The initial 93 focused
tests passed, followed by 275 combined graph, relay, activation and documentation
tests, full resource validation and inline Action agreement. Markdown, strict
MkDocs/source-built checks (220 pages), all 11 CI generator checks plus the
hook-only remediation-target check, changed-file pre-commit hooks and
git diff --check also passed without generated drift. No stages, schedules, Actions or legacy webhook
policies changed. At that checkpoint the correction was unmerged; the subsequent
merge/re-preview is recorded above. It still has not been imported into live
resources. A one-off UI edit is not a durable substitute.
Input and permission preparation¶
Read-only file-presence checks found five missing fingerprints: incident adapter,
Paperless–Stirling bridge, Firefly, Orders and PR Dashboard. LiteLLM and Reddit
fingerprints existed as private 1001:1001 0600 files, but existence does not prove
freshness or validity. The host checkout was 8bf7aa5, not the earlier
activation-time 0416d8e; preserve #511 and unrelated host work. No checkout,
rendering, fingerprint refresh or host Repo pull was performed by this preflight.
Prepare/verify required inputs only in the separately approved window before Sync
adds the tracked files; cache refresh can otherwise retain missing-file errors.
Native ListPermissions plus GetPermission on all current grants confirmed
36 Read + 2 Execute, with no effective-level differences or specific grants.
The enabled homelab-deploy identity remained non-admin/non-super-admin, with empty
base permissions and server/build creation disabled. Desired post-bootstrap access
is still 51 Read + 5 Execute: add 15 Read and 3 Execute grants, not broad
Write/admin/Action Execute. The additional Execute roots are only
deploy-reviewed-images-edge, deploy-reviewed-images-documents, and
deploy-reviewed-images-finance; existing Execute stays on the Sync and deploy-infra.
At that checkpoint the next action was review/merge of the guard PR. That is now complete; obtain separate approval for input preparation and external bootstrap/grants, drain independent writers, then verify the exact Sync Update before adding only the reviewed grants. Sync can register schedules and refresh Stack caches; it is not an inert file import or a deployment-success receipt. The new daily native image check is check-only; no immediate workload deployment is requested by the reviewed source. Optional App/Core-token provisioning, control-plane recreation and workload deployment remain later gates.
Private redacted evidence is retained as
/etc/homelab/komodo-deploy-bootstrap-20261026/bootstrap-preflight-c9a0fd7.json.
Deployment stayed disabled, the runner stayed online, and no timers, markers,
permissions, runtime configuration or control-plane containers were changed.
Natural convergence acceptance complete¶
On 2026-10-26, the owner authorized observation and a docs-only follow-up PR, not deployment. The 18:24 UTC inspection found eight successful normal apply recaps since activation, including 18:12:11–18:23:15 UTC. The actual runner's credential checks still passed, but historical apply logs could not establish a credential read specifically during the restrictive parent mode. A bounded, read-only observer therefore watched the next scheduled cycle without forcing it.
| Observation (UTC) | Evidence |
|---|---|
| 18:26:17, after the previous full apply | Actual runner passed at /etc/homelab = root:root 0755 |
| 18:54:43 | Next timer-driven apply started naturally; no timer, marker or service changes by the observer |
| 18:57:37–18:57:38 | Parent naturally became root:docker 0750 (GID 987); actual runner passed credential/access/authentication checks in that state |
| 19:01:44 | Parent naturally returned to root:root 0755 |
| 19:04:57 | Full apply completed, exit 0: ok=439, changed=29, skipped=98, failed=0, unreachable=0 |
| 19:05:00 | Actual runner passed again after the completed apply at root:root 0755; observer finished within its one-hour bound |
All three probes used the same actual container, cdd2e124a9d2…, started at
15:09:30 UTC, runner ID 25235. Each checked the default UID/GID 1001:1001,
groups [1001,10002], no Docker group/socket, all capabilities dropped and
no-new-privileges, and the same-path read-only /etc/komodo-deploy directory bind.
Host SOPS/output and actual-container validation returned 0; credential read passed,
directory listing was denied, and write-open returned EROFS without writing.
HTTPS GET /user authenticated enabled, non-admin/non-super-admin homelab-deploy.
The shared parent was stable before/after each probe, and container identity was
unchanged. No credentials or raw API responses were exported.
This closes the previously outstanding both-mode and post-activation convergence
gate. Private state now records runner_qualified_bootstrap_pending, with
both_parent_modes_verified and post_activation_convergence_verified true. The
root-private evidence is
/etc/homelab/komodo-deploy-bootstrap-20261026/natural-convergence-20261026T182615Z.jsonl.
The observer did not chmod the parent, force an apply, pause timers, create/remove
maintenance or drain markers, restart/register the runner, rotate credentials or
invoke an execute endpoint. Its only writes were private observation records; the
subsequent state update records the result, not a deployment enablement.
Two separate operational facts must not be conflated with this acceptance:
- Since the initial activation snapshot, four runner listener exits returned 0,
followed by systemd restart/reregistration and listening for jobs. The current
runner/container IDs differ from the historical
24873/f469fae…pair; that is not evidence of credential-preflight failure. The observed cycle retained the same current container throughout. - The 17:50:00–17:51:58 UTC check-mode invocation exited 2 at
operator-shell : Set operator shell home, reporting an undefined value (ok=135, changed=3, skipped=76, failed=1). This unrelated failure was not fixed. The successful full-apply recaps and credential probes do not establish that every host check is clean. The shared-parent role conflict and labctl traversal also remain a separate unresolved issue; only Komodo's dependency was removed.
Deployment intake remains disabled, normal timers remain active, and maintenance,
drain and relay-pending markers remain absent. Core/Periphery retain their original
2026-10-25 11:33 UTC start times. No ResourceSync, grant change, workload deploy,
image pin update or optional Core/publisher credential provisioning was performed.
Next is separately reviewed external bootstrap of the merged 26 Procedures / 2
Actions and least-privilege grants, then separately approved deployment/application
acceptance. PR #512 published the activation and acceptance record;
it does not change inventory, runtime scripts, secrets or deployment controls.
The docs branch is based on later main 8bf7aa5 (#511), preserving its backup
and LiteLLM changes. The acceptance-time host checkout remains 0416d8e;
updating this docs branch did not advance or deploy the host.
Runner activated: remaining convergence gate¶
This historical activation checkpoint predates the completed convergence evidence above. The owner authorized waiting up to 30 minutes, then continuing the approved runner-only activation. The earlier apply finished normally at 2026-10-26 12:01:20 UTC, exit 0; no backup/restore work or Ansible process was interrupted. The check service's last invocation at 11:49 UTC also exited 0, but this alone does not distinguish a full check from a lock-related skip and is not all-checks-passed convergence evidence.
- Rechecked deployment disabled, runner offline/not busy, no queued/running/waiting repository workflows, and no pending relay intent. Read-only native API checks confirmed the existing 10 Procedures, 6 Repos, 21 Stacks, 0 Actions and Sync were idle with no nonterminal Updates. No execute endpoint was called.
- Recorded a fresh private activation window, created an owned maintenance marker
and paused only the two pull timers. Held both pull and relay locks for the
verified-bundle fast-forward from
9240794to0416d8e. There were no incoming local conflicts. All four unrelated tracked edits were byte/mode/owner-identical afterward, and the observed local-file set remained intact. No staging, reset, stash, cleanup or backup-artifact relocation was used. - Effective combined inventory confirmed the three persistent flags true only on
infra-services, with only
homelab-lan-01allocated. The narrow common credential apply reported ok=8, changed=0, skipped=2, failed=0; both renderer checks returned 0. The original credential and legacy copy were retained unchanged. - The runner-only wrapper excluded
github_runner_observabilityand selected onlyhomelab-lan-01: ok=39, changed=3, skipped=6, failed=0. Changes were the installed launcher, runner Docker-GC helper file and starting the selected service. No image rebuild or GC operation occurred; the GC timer stayed disabled on LAN. The owned drain marker remained present, so the new launcher waited with no container/registration during configuration. - After successful apply and fresh queued-work checks, released only the owned
drain marker at 12:05:55 UTC, retaining its private archived content. No
redundant service restart or adapter
resumewas issued. The 90-second GitHub registration wait expired; immediate follow-up found the runner online/idle, with zero service restarts and no launcher error signals. Registration was not retried. Runner ID: 24873; container started 12:06:26 UTC, imagelocal/github-actions-runner:2.336.0-r5. - Actual-container checks used its default UID/GID
1001:1001, groups[1001,10002], no host Docker GID 987 or Docker socket, all capabilities dropped and no-new-privileges. The same-path/etc/komodo-deploydirectory bind was read-only. Host SOPS/output check and actual-container--validate-existingreturned 0; credential read succeeded, directory listing was denied, and write-open returnedEROFSwithout writing bytes. HTTPSGET /userauthenticated as enabledhomelab-deploy, non-admin and non-super-admin. No secrets were logged. - The actual runner observed
/etc/homelabasroot:root 0755only. Its new-path access under a naturally occurringroot:docker 0750state, and after subsequent normal convergence, remain unverified. Do not change the live parent permissions or rotate credentials to manufacture acceptance. #508's isolated lifecycle tests and the earlier temporary preflight remain separate evidence. - Removed only this window's owned maintenance marker and restored both timers to
their prior active states. Runner service remains active with zero restarts;
GitHub reports online/not busy. Drain marker and relay pending intent are absent.
Private phase is
runner_activated_pending_convergence. At the 12:09 UTC inspection, the next normal apply was scheduled for 12:32:44 UTC; recheck the schedule before observing rather than forcing a broad apply.
Private records under /etc/homelab/komodo-deploy-bootstrap-20261026/ include
activation-window-0416d8e.json, activation-checkout-preservation-0416d8e.json,
the two phase logs/result receipts, released-drain-marker-0416d8e.json, and
actual-runner-acceptance-0416d8e.json. Keep these and all legacy staging/credentials
until separately approved cleanup.
Core/Periphery still have their original 2026-10-25 11:33 UTC start times and
:2 containers. No ResourceSync, Procedure, permission grant, workload deployment,
image pin update or optional image-publisher/Core credential provisioning occurred.
At this checkpoint, deployment intake stayed disabled through remaining convergence
observation and later bootstrap/grant/deployment gates. Handoff docs and agent
guidance were prepared locally for PR #512. Post-activation
validation passed: 94 focused activation/documentation tests, Markdown fix/check,
strict MkDocs and source/built-site checks (220 pages), and git diff --check.
These documentation checks do not close the live convergence gate.
Activation review complete — waiting for convergence¶
This historical waiting checkpoint precedes the successful activation above.
PR #510 merged at
2026-10-26 11:27:39 UTC as
0416d8ead3e4cdfc78577a877172c2a3b09dbeaa. All 26 PR-head checks, including the final
CI gate, completed successfully before merge. The owner's review/merge gate is
complete; the remaining prerequisite for the already-approved runner window is a
fresh idle/concurrency preflight, not another review of the same flags.
- A normal
ansible-pull-apply.servicerun started at 11:22 UTC and remainedactivating/startafter a bounded 180-second idle wait. The waiter returned 75 (still busy); the production apply was neither killed nor interrupted. No new maintenance marker was created or timers paused in this follow-up. /opt/homelabwas rechecked at9240794with the four unrelated tracked edits and observed host-local files intact. Normal ansible-pull uses a separate/var/lib/ansible-pull/homelabcheckout: its progress, or its consumption of the merged common settings, does not establish/opt/homelabadvancement or runner activation. The ordinary site playbook does not applygithub_runner.- Workflow
komodo-deploy.ymlremains disabled.homelab-lan-01remains offline, not busy, with no container and the owned drain marker present. No relay pending intent was present. Private state now recordsactivation_reviewed_waiting_for_convergenceandactivation_applied: false. - Prepared and verified a Git bundle containing only the reviewed
9240794→0416d8eadvance; staged it under.git/komodo-cutover-20261026/. Staged the root-privateactivation-0416d8e.ymlwrapper under the existing bootstrap directory. These are preparations, not a checkout update or Ansible apply. - The wrapper selects 10 common credential tasks only with the existing
komodo-deploy-credentialstag, then onlygithub_runnerwith the explicithomelab-lan-01allowlist. Unlike the stock runner playbook, it does not importgithub_runner_observability. Source path overrides account for its private location; all three activation flags still come from persistent inventory. Task listing and all five activation tests passed. No runner-role apply occurred. - Read-only prerequisites passed: working Docker; expected checkout ownership; dedicated credential metadata unchanged; pinned cached runner image provenance; installed Dockerfile/entrypoint matching reviewed source; pinned SOPS checksum; required registration ciphertext, host identity and labctl file present. No credential values were printed. These checks do not establish registration or actual-runner acceptance.
Resume only after convergence finishes: repeat the idle/queued-work checks, acquire the appropriate locks, preserve host files while advancing to reviewed main, apply common credential provisioning, then apply only the LAN runner role with its owned drain marker retained. After successful apply and fresh registration preflight, release that marker deliberately. The waiting new launcher continues; do not add a redundant service restart or replay the earlier timed-out drain. Deployment remains disabled through actual-runner/convergence acceptance and the separate bootstrap/grant/deployment gates. At that checkpoint, the updated handoff was local pending this follow-up publication; merged activation settings were unchanged.
Approved cutover checkpoint — 2026-10-26 UTC¶
This historical checkpoint precedes the #510 merge recorded above. The owner
explicitly approved cutover. Work proceeded only through credential migration and
activation-PR preparation; the next boundary then was owner review/merge before
runner recreation or registration. Bootstrap and workload deployment remain
separate gates. The isolated clone is on ops/komodo-credential-activation; the
original dirty checkout and unrelated work remain untouched.
- Recorded original control states privately in
/etc/homelab/komodo-deploy-bootstrap-20261026/cutover-state.json, then disabledkomodo-deploy.yml, paused Ansible pull timers and created an owned maintenance marker. No pending relay intent was present. - Drained idle
homelab-lan-01through the native maintenance adapter, after checking GitHub busy=false and noRunner.Worker. Its 30-second stop call timed out; subsequent inspection confirmed the container was gone and the service stopped with exit 137. No job was interrupted. The adapter did not write a success replay receipt; do not blindly replay drain or interpret systemd's failed status as a running container. The drain marker remains at/var/lib/github-runner/drain/homelab-lan-01. - Allowed the already-active Ansible apply, including restore-drill bootstrap work,
to finish normally at 10:51 UTC (
Result=success, exit 0). It was not interrupted. The check service's exit 2 at 05:48 UTC predates cutover and remains unresolved; restored timers are not evidence that every convergence check passed. - Staged the renderer and ciphertext from immutable merged #508 under private
merged-6ab7660/; verified their bytes also match newer main9240794. Explicit root-only--migrate-legacyreturned 3 (changed). Private comparison confirmed the new and retained legacy credentials match approved SOPS; no values were logged, rotated or recaptured. New directory:/etc/komodo-deploy,root:1001 0710; file:komodo-deploy.env,1001:1001 0600. - A temporary, automatically removed container from the configured cached
local/github-actions-runner:2.336.0-r5passed read-only preflight: UID/GID1001:1001, supplementary 10002, no Docker GID 987, all capabilities dropped, no-new-privileges, same-path read-only directory mount. Credential read succeeded; directory listing was denied and write-open returnedEROFS. HTTPSGET /userauthenticated as enabled, non-adminhomelab-deploy. This was not a registered or recreated runner and is not actual-runner acceptance. - The generic safe-pull attempt timed out after 90 seconds before advancing HEAD.
It was not retried with a longer timeout and its source was not modified. A tested
targeted fast-forward instead held both Ansible-pull and relay locks and used a
verified local Git bundle. Only dirty
services/rexit/compose.ymloverlapped incoming main; its bytes already matched incoming main. It was archived and only that identical path staged, without a worktree reset, before fast-forwarding from034fe1dto924079477549e3962a3f4b2c6c9073aeb7fdaf72. #509's LiteLLM credential-rendering changes were retained. - Unrelated tracked edits remained byte/mode-identical:
backups/restore-drills/rexit.py,backups/restore-test.sh,scripts/prox-mem-soak-finalize.sh,scripts/prox-mem-soak-sample.sh. Observed host-local files and backup artifacts remained in place. Private preservation records, the Rexit archive and checkout output remain under the bootstrap directory; Git bundles remain in.git/komodo-cutover-20261026/. Nothing was broadly reset, stashed, cleaned or removed. - The actual merged
/opt/homelab/scripts/render-komodo-deploy-env.pysubsequently returned 0 for both--checkand--validate-existing. - Removed only this cutover's maintenance marker after verifying its content and
ownership; restored both Ansible pull timers to their prior active state. Private
state is now
awaiting_activation_review. Fresh inspection confirmed the deployment workflow remainsdisabled_manually, the runner offline/not busy with no container, the drain marker present, and no relay pending intent. Routine convergence is not held while waiting for review.
This activation proposal enables all three flags only in persistent
infra/ansible/inventory/host_vars/infra-services.yml:
common_komodo_deploy_api_reviewed, common_render_komodo_deploy_env, and
github_runner_komodo_deploy_api_enabled. Role defaults stay false. The only
allocated runner on that host is homelab-lan-01. These source settings have not
been applied; the host remains on pre-activation 9240794 at this checkpoint.
Core/Periphery retain their original 2026-10-25 11:33 UTC start times and existing
:2 containers (observed version 2.3.4). No ResourceSync, Procedure, workload deploy,
new grants, image pin update or optional publisher/Core credential was invoked or
provisioned. Live bootstrap from 10 Procedures / 0 Actions to the merged
26 Procedures / 2 Actions remains pending. Keep legacy credentials and private
staging/archives until separate cleanup approval.
At that checkpoint, next was review of the activation PR and CI, then coordinating the narrowly scoped common
credential task and only homelab-lan-01 runner maintenance from reviewed merged
inventory. See activation review and runner resume.
Do not dispatch deployment or bootstrap merely because the migration passed.
Activation proposal validation¶
- All five activation tests passed locally, including effective Ansible inventory from both inventory files, strict host/group/inline declaration checks, disabled role defaults and the sole LAN-runner allocation. These are configuration tests, not an Ansible apply or live-runner acceptance.
- Local relay, operational-doc, inventory-generator, credential-activation and #509 literal-rendering regressions: 220 cases, 219 passed, one opt-in Compose skip. Initial documentation wording assertions were corrected without weakening tests.
- Disposable Linux Python 3.13.17/Bash 5.2.37, UID/GID 1001:1001: 576 cases, 567 passed, nine skipped, across Komodo (364), relay (88) and documentation (124). The snapshot excluded actual secrets and Git metadata; the container had no network, host credentials or Docker socket, with capabilities dropped and a read-only root. Temporary artifacts were removed. The nine skips cover four opt-in Docker lifecycle probes, three native-Ansible cases, one Compose case and one root-only UID boundary. Prior #508 lifecycle evidence remains separate.
- All four safe-pull tests passed on Linux. Their initial local failures were the
macOS Bash 3
declare -Alimitation, not evidence of production-helper success or a reason to modify its implementation. An initial Linux harnessnoexectmpfs issue was corrected in the disposable environment only. - All 11 CI generator checks plus the hook-only remediation-target check passed without generated drift. Inline Action agreement and Komodo resource validation passed (21 Stacks, 6 Repos, 26 Procedures; two inline Actions).
- Markdown fix/check, strict MkDocs and source/built documentation checks passed
for 220 pages. Changed-file pre-commit hooks passed, including YAML/Ansible,
owner-reference, runner-topology and Markdown checks;
git diff --checkpassed. Activation PR CI and actual-runner acceptance remain separate gates.
Scope and packaging status¶
The owner approved all high/medium work in the capability audit, and later explicitly authorized an isolated branch, commit, push and pull request to package the change. That authorization is not a deployment or merge instruction.
The original working tree starts at 4e7a8d1 and contains both the Komodo proposals
and unrelated work. Initial implementation compared selected runtime/test files
with production fec3dbc720289c7371eeb6654b1f6f4dc9817241, preserving Reddit's
existing optimization. Remote main subsequently advanced to 7e7f79b6 and includes
newer operational/secret/recovery documentation plus manual-only dispatch policy.
PR #506 packaging used a fresh isolated clone at artifacts/komodo-deployment-pr,
based on 7e7f79b6, on branch feature/komodo-deployment-uplift. The fresh clone avoids the
original checkout's malformed origin/main 2 ref; the original working tree is
untouched. Proposals were merged selectively, not copied over newer-main contracts.
Implementation commit 1f7d483f485f2fcc858ab65ae48557de8c3cd620 is pushed and
PR #506 was merged by the owner at
2026-10-26 09:26:10 UTC as 6d6ff2ef028abfabb31ea392673fbd5a33902916.
All 26 merged-head checks passed. Integrated local validation is recorded below;
the merge is not workload rollout acceptance. PLAN.md, unrelated
documentation/history/navigation and workload placement are unchanged. The later
image-update source extension is recorded separately below; it is not included in
the earlier implementation commit or validation counts.
| Area | Source contract | Live state |
|---|---|---|
| Input-aware deployment | Six additional private HMAC profiles, render-before-compare, predeploy verification, conditional selection | All seven input profiles verified; first deployment/application acceptance pending |
| API relay | Exact Update completion, bounded deadline, no blind retry, persistent lock/intent, full managed visibility and busy checks | #508 migration and #510 persistent activation complete; actual-runner credential/both-mode/natural-convergence acceptance passed; external bootstrap and 56-grant scope verified; first deployment acceptance pending |
| Isolation | Independent host chains and independent application children; Firefly before apps | Graph synced; first execution/failure-isolation acceptance pending |
| Versions | Matched Core/Periphery/helper 2.3.4 pins and pinned LXC installer recipe | All four versions observed 2.3.4; helper tag cached; Compose pin application pending |
| Bootstrap ownership | Earlier three bootstrap schedules declared; new extension replaces legacy image schedule with daily native check, still check-only | Adopted and verified through the approved external Sync; scheduled execution acceptance remains separate |
| Missed triggers | Explicit operator-reviewed manual workflow recovery | No autonomous full-deploy schedule introduced |
Post-merge checkpoint — 2026-10-26 UTC¶
- Merge verified at
6d6ff2e; all 26 checks on the merged PR head passed. - The host remained at
034fe1dduring inspection, with unrelated tracked edits and host-local files. They were not reset, stashed, removed or moved. - Live Komodo still contains 10 Procedures, 0 Actions and 1 Sync. The merged
26 Procedures and 2 Actions have not been bootstrapped. Core/Periphery remain
on their existing
:2containers, unchanged since 2026-10-25 11:33 UTC. - Manual deploy run 40299611768
failed at credential loading with
Cannot read private Komodo API credential file. This was before host fast-forward, ResourceSync or Procedure execution; no relay pending intent was created. No automatic retry or permission workaround was applied. - The legacy file
/etc/homelab/komodo-deploy.envitself remained1001:1001,0600. At this dated capture the LAN container actually ran as1001:1001with supplemental group 10002, not host Docker group 987. It could not traverse/etc/homelabwhile that directory wasroot:docker,0750. During the active Ansible apply, the directory changed toroot:root,0755, and the same container could read the file again. No credential rotation was needed. - Source evidence:
common/tasks/ansible-pull.ymlandmonitoring-deadman/tasks/main.ymlmanage the shared directory asroot:root,0755;operator-tools/tasks/labctl.ymlmanages it asroot:docker,0750. Stable credential access must be fixed declaratively and tested across convergence. Do not add the runner to Docker, make credentials broadly readable, or rely on a temporary directory chmod. - Read-only image run 40299854748
succeeded on merged main for
edge; request UUID3a3c855b-381b-4787-b625-3c6fb6ec22ab. The publish job was skipped and the report's file list was empty. It reported Traefikv3.7→v3.7.16, and initial digest-pin proposals for unchanged Homepagev1.13.2, AdGuardv0.107.79and Unbound1.22.0. No pins, update PR, image layers, Sync or deployment were produced. - The image-update App and optional Core dispatch token remain unprovisioned. Runner-access correction and the controlled bootstrap are the next gates.
These are historical observations before the approved cutover checkpoint above. At this earlier checkpoint, only follow-up PR preparation was authorized.
Credential-access follow-up — 2026-10-26 UTC, source only¶
The #508 source-preparation work used the isolated
artifacts/komodo-deployment-pr checkout on fix/komodo-runner-credential-access.
The original checkout and unrelated work are
untouched. The five already-dirty checkpoint docs are retained; old path/runtime
observations above and below remain dated history, not rewritten as new-path evidence.
This follow-up has separate validation evidence; PR #506's earlier counts do not
validate the correction. Source preparation does not authorize production cutover.
Follow-up validation¶
- Full Linux Python 3.13.17 discovery: 2,056 passed, zero failures/errors;
37 cases skipped for opt-in or unavailable native tools (2,093 cases discovered).
The run used an unprivileged, network-disabled disposable snapshot without host
secrets, host mounts or a Docker socket. Missing
ssh-keygenwas supplied only during disposable image setup; no production dependency was changed. - All 4 explicit Linux/Docker lifecycle probes passed, separately from the
default-skipped suite: 19/19 new-path reads across reordered/repeated/partial old
parent policies; the old path reproduced
EACCESin all 11 restrictive-mode checks. - 32 atomic replacements remained visible on subsequent opens with unchanged
directory inode and no partial outputs. Eight injected precommit failures
preserved previous state. The read-only directory-mounted runner saw replacements
without restart; owner write/delete returned
EROFS, directory listing/entry mutation were denied, and unrelated UID 10001 could not read the credential. - The probes used only synthetic values, UID/GID 1001 plus group 10002 (no Docker group), zero effective capabilities and no network. UUID-labelled test containers and volume were removed after validation; no live host was involved.
- Focused local suites: 299 tests, no failures/errors, one Linux-only skip. Migration fault tests separately passed under Linux UID 1000 and UID 1001 so ownership-injection coverage does not depend on the CI runner's identity.
- Strict redacted Gitleaks staged-diff scan: zero findings, no new allowances. The original real encrypted API source and its key material were not changed.
- Markdown and strict MkDocs/source-built documentation checks passed (220 pages).
Changed YAML/Ansible tasks passed lint; existing configuration/line-length notices
remain warnings.
git diff --checkpassed. - Independent security review found no high-confidence code blocker. Review/merge, persistent opt-ins, actual-runner convergence acceptance and deployment remain separate owner-controlled steps.
PR #508 CI fixture correction¶
Both failing test jobs in run 40305132743
failed the same migration-fixture assertion. mkdir(mode=0755) was filtered by
CI's restrictive umask to 0750, but the test expected unchanged 0755. Migration
correctly rejected the unsafe directory; this was a fixture setup assumption,
not a production permission repair or credential-access failure.
The fixture now explicitly sets its intended mode after private creation. It checks
that migration leaves unsafe 0755, 0750 and 0770 directories untouched, and
its mode helper is tested under umasks 0022, 0027 and 0077. All 20 migration
tests passed under each mask; 73 focused tests passed under 0027 with one Linux-only
skip. A disposable Linux Python 3.13.17 run as UID/GID 10001 with umask 0027
passed all 357 Komodo tests, with eight optional/native-tool skips. No production
code, runner umask, host permissions or security checks were relaxed.
PR #508 merge and cutover readiness¶
PR #508 merged at 2026-10-26 10:37:20 UTC as
6ab7660333766c5a2e27bcba31c0030e2e163d40; all 26 PR checks passed. Read-only
post-merge inspection found the host checkout still at 034fe1d, the legacy API
file still 1001:1001 0600, and /etc/komodo-deploy absent. The LAN runner is online
and idle, with no drain marker or relay pending intent. Core and Periphery retain
their original start times. An Ansible apply is active, so cutover must coordinate
with it rather than race a checkout update or stop it midway through a task.
Unrelated tracked edits and host-local files remain untouched. Of the observed tracked dirty paths, only Rexit Compose overlaps the incoming change set; its bytes matched incoming main at inspection. Recheck all conflicts at cutover; this is not permission to reset, stash or discard host work. The three persistent activation flags are not set on the LAN host; defaults remain false.
No migration, runner recreation, Sync, deployment, maintenance flag or timer change was performed in this readiness pass. The next owner gate is approval for the controlled credential/runner cutover and its persistent activation/documentation change. Workload deployment and control-plane recreation remain separate gates.
Source contract and cutover¶
The source correction removes Komodo's dependency on the shared /etc/homelab
parent rather than changing its conflicting roles. The root:root 0755 versus
root:docker 0750 policies and labctl traversal remain a separate unresolved issue.
The legacy /etc/homelab:ro mount is preserved for labctl. No shared-parent chmod,
Docker group membership, broader file access or credential rotation is part of the
fix.
- New default/API file:
/etc/komodo-deploy/komodo-deploy.env,1001:1001 0600. Its dedicated parent isroot:1001 0710, giving group search/traversal only, not listing/write. Renderer validation rejects incorrect owner/group/mode, extended/default ACLs and ancestor symlinks even for check/unchanged operations. Unsafe existing directories are not repaired. - Enabled LAN preflight and actual runner use a same-path read-only directory
--mount, which fails on missing source. Never bind the file inode; directory binding preserves visibility of atomic replacements. - Explicit root-only
--migrate-legacyverifies the existing old API pair equals reviewed SOPS before creating the new directory/rendering. It refuses a different credential at the new path or an unsafe existing directory. Old file, ciphertext and age key stay untouched; no rotation or recapture. CLI status: 0 unchanged/valid, 3 changed/would change, 1 failure.--checkhas no filesystem mutations and is not a migration dry-run. - All three flags remained false in PR #508:
common_komodo_deploy_api_reviewed,common_render_komodo_deploy_env,github_runner_komodo_deploy_api_enabled. A future cutover must enable them in reviewed persistent LAN-host inventory, not one-off extra vars alone, applying common provisioning before runner changes.
At that source-only checkpoint, the future controlled cutover
requires separate approval. Pause new deployment dispatch and drain the LAN runner
and independent writers first; verify fresh main and stage trusted merged
code/ciphertext via the authorized host checkout process, preserving unrelated dirty
and host-local files. Do not bootstrap through the broken relay. Run migration, then
read-only validation, then the reviewed persistent opt-ins. Recreate only the drained
LAN runner when approved, not Core/Periphery/Mongo or workloads. Preserve
/opt/homelab/.git/komodo-deploy/, including lock and pending state.
Before bootstrap/workflow dispatch, verify the actual default UID/GID and supplementary
groups (not a root or --user override), read-only same-path directory mount and
current SOPS-matching credential with private read-only authentication. Repeat after
both historical shared-parent modes and authorized convergence; deliberate alternation
and atomic-replacement tests belong in isolated fixtures. A point-in-time helper
read or successful migration is not convergence or application acceptance.
Rollback of a consumer retains the new mount and explicitly sets the existing
KOMODO_DEPLOY_ENV_FILE=/etc/komodo-deploy/komodo-deploy.env. Reverting to old mounts
restores the traversal bug, so dispatch stays paused. Legacy credential/bootstrap
cleanup needs separate explicit approval and is never automatic migration cleanup.
That #508 source-only pass performed no migration, inventory enablement, runner
recreation, credential rotation, bootstrap or deployment. The later approved cutover
checkpoint above supersedes its migration status, not its remaining acceptance gates.
Guided preparation — 2026-10-26 UTC¶
- Cached
ghcr.io/moghtech/komodo-periphery:2.3.4on infra-services, matching running digestfb4b52961fcce7a31e3776930e422967b6640f653de876f59f26dea243e29beb. Core/Periphery start times and running image IDs remained unchanged. - Remote GitHub main is
7e7f79b6; the host checkout remainsfec3dbc. Remote main is already explicit main dispatch only. The API workflow must preserve this policy, including case-sensitive Bash checks before checkout; no push trigger is restored. - Live GitHub
homelab-productionhas empty protection rules and no deployment branch policy.mainreports unprotected; ruleset APIs returned HTTP 403 with a plan-upgrade requirement. Native reviewer approval was an unverified assumption, now corrected. No protection, billing or visibility settings changed. - Created non-admin Service User
homelab-deploy(6adf0356a10ff54a58019c59) through supported Komodo UI. Saved grants were re-read: 38 total — 36 Read, 2 Execute, only Syncinfra-services-01and Proceduredeploy-infra. There are no specific permissions; base permissions are empty and server/build creation is off. New Procedures need Read grants after bootstrap. - The owner created the Service User's API pair and captured it with the hidden-input
helper. Local
services/komodo/deploy-api.sops.yamlpassed ciphertext-only validation; no plaintext credential was displayed or stored in the checkout. Preserve that existing encrypted source rather than minting another pair for rollout. - Staged only renderer/ciphertext under root-private
/etc/homelab/komodo-deploy-bootstrap-20261026/, outside the live checkout. The host age identity decrypted it. A root-only temporary candidate was checked with read-only/user,ListPermissionsandGetPermissioncalls: enabledhomelab-deploy, non-admin/non-super-admin, empty base permissions, 36 effective Read levels and exactly the two approved Execute entry points. - Atomically rendered
/etc/homelab/komodo-deploy.envas UID:GID 1001:1001, mode 0600. Ciphertext/runtime consistency passed. The runninggha-homelab-lan-01container, explicitly as UID:GID 1001:1001, could read it and authenticate through read-only/user. The temporary plaintext candidate was removed; root-private staging retains only renderer/ciphertext material. - The host checkout remains
fec3dbc; Core/Periphery start times and images are unchanged. No Sync, Procedure, deploy or restart was needed for credential verification. At that checkpoint, declarative Ansible enablement, code merge, ten new Procedure Read grants, reviewed bootstrap and application acceptance remained pending. The later extension increases the desired inventory/grants as recorded below; no additional live grants were applied. - v2.3.4 permission review found that Sync and parent Procedure use internal administrative identities for their work. The caller needs Execute on the two entry points, not blanket downstream Write/Execute. Reviewed definitions remain a trust boundary; direct denial on a child cannot sandbox a changed parent.
What changed¶
- At the initial uplift stage,
resources.tomldefined 20 Procedures (now 26 plus 2 Actions in the source extension below). The recursive general graph selects each of the 19 automatic stacks once, conditionally. Komodo/OneUptime exclusions and the exact private canary remain intact. - LiteLLM, the incident adapter, Paperless–Stirling bridge, Firefly, Orders and PR Dashboard reuse the private keyed fingerprint engine. No secret environment bytes are exposed as native tracked files. Dedicated force paths remain; new local-stack retry paths are explicit and unscheduled.
- LiteLLM copies non-secret
config.yamlinto its built image. A config-only change changes the proxy image rather than retaining a stale single-file mount or forcing database recreation. Its shared validated secret-rendering and PostgreSQL backup/recovery contracts must survive runtime integration. - General deployment uses the native API and waits on exact Sync/Procedure Update IDs. A fixed delay, HTTP acknowledgement, missing resource or unknown outcome cannot count as success. New resources require reviewed external bootstrap and Read grants before the relay can see the complete declared inventory.
- Isolated SOPS API-credential rendering and runner validation are opt-in, preserve existing credentials on pre-replace failure, and add no runner Docker socket.
- Validation traverses nested Procedures, checks same-host pull ordering, rejects disabled/unreviewed execution shapes, and covers the corrected shell guard.
- Docs preserve newer-main owner-gated bootstrap, fail-closed secret rendering,
coordinated database/shared-secret rotation, Periphery-only
--no-depsrecovery, LiteLLM logical recovery and Paperless uncertain-outcome recovery. The socket checklist now points to canonical maintenance instead of a generic Compose retry. - Initial uplift navigation retained existing newer-main entries and added only the capability audit and two Komodo journals; the extension also links its image-update runbook. Manual-only dispatch is retained throughout; the GitHub environment name is not presented as a native approval gate.
Image-update extension — 2026-10-26, not live¶
A later source-only addition in the isolated PR #506 clone brings resources to 26 Procedures + 2 Actions, with 6 Repos and 21 Stacks unchanged. It adds six Procedures to the earlier 20; against the original ten live Procedures this is now sixteen new Procedures plus two Actions. No bootstrap, new grants, image-update token creation, Core recreation, pin write, PR publication, merge or deployment occurred in this documentation pass. The existing deployment API preparation above remains real and unchanged; it is not evidence that image-update credentials are provisioned.
flowchart TD
N[Daily native same-tag check] --> R[Safe results or incomplete failure]
O[Manual registry check or confirmed proposal] --> A[Inline Komodo Action or manual GitHub dispatch]
A --> C[Bounded registry metadata checks]
C --> P[Dedicated App creates draft PR when requested]
P --> H[Human diff review and ordinary PR CI]
H --> M[Human merge]
M --> W[Separate explicit deployment workflow]
W --> S[Exact successful external Sync]
S --> D[Chosen cohort pull then conditional whole-stack reconciliation]
check-image-updatesowns daily 03:00America/Los_Angeles, invoking its inline Action for 14 fixed native checks. Only same-tag digest changes are checked; digest-pinned/local-build images are unsupported and incomplete accounting fails. Native false means not detected or unknown, never current. LegacyGlobal Auto Updateremains manual with schedule disabled andskip_auto_update = true. No Procedure enablesauto_updates = true.- Manual
check-image-versions→request-image-pin-updates→ GitHubcheckis report-only. Manualpropose-image-pin-updatescarries the exactPROPOSE_IMAGE_PINSconfirmation in its definition and requests GitHubpropose, never auto-merge/deploy/force. The Procedure is not an additional interactive prompt. Weekly Dependabot is unchanged; this complements it on demand, not a major resolver. - Registry policy covers 13 image fields in six stacks across edge (Traefik, Homepage, AdGuard), documents (Paperless, Stirling) and finance (Firefly). It selects same-minor patches with prefix/suffix preserved. Major aliases use same-tag digest-only refresh, with no semantic patch guarantee. Immutable OCI index/manifest digests are verified; private registries, local builds, digest-only/unsupported refs, errors and truncation fail closed rather than producing false up-to-date claims or partial pin edits.
- Only allowlisted Compose image fields, literal Quick Reference Image table refs and
future
services/komodo/image-update-history.mdentries may change. Unmatched docs warnings need humans; no arbitrary docs rewrites. History is header-only now, with no fabricated proposal/deployment entries. Rexit, OneUptime, Komodo and local source updates are excluded from the writer; private Rexit remains native-check-only with safe unknown metadata permitted. - Both
.tsAction sources are generated into inlineresources.tomlbyscripts/render-komodo-actions.py; CI checks--check. Core has no code-checkout mount and must never execute runner-writable paths. The optional Core env file is absolute/etc/homelab/komodo-core/image-updates.env, root-owned parent 0700/file 0600, rendered byscripts/render-komodo-image-update-env.pyfrom future SOPS ciphertext; only the example exists. The Core PAT needs repo Actions:write, not Contents:write. Its authority is broader than one workflow; fixed reviewed code enforces the whitelist. Provisioning and controlled Core recreation remain pending. Manual GitHub check needs no Core token or publisher credentials. - The publisher requires a dedicated GitHub App installed only on this repo with
Contents:write and Pull requests:write,
vars.IMAGE_UPDATES_APP_CLIENT_IDandsecrets.IMAGE_UPDATES_APP_PRIVATE_KEY, all unprovisioned. There is noGITHUB_TOKENfallback so App-created draft PRs trigger ordinary CI. The Action supplies a UUIDrequest_id, verifies exactKomodo image updates / op / cohort / uuidcorrelation within 35 minutes and never blindly retries an unknown outcome. - The existing result-aware deployment workflow offers default
deploy-infraplusdeploy-reviewed-images-{edge,documents,finance}. Each new root pullshomelabbefore an exact conditional batch, never direct UI deployment. Cohorts reconcile whole changed stacks on current main, including non-image changes: review all relevant diffs and drain independent writers, not just review the image PR.
The live relay identity still has 38 effective grants: 36 Read + 2 Execute. Desired post-bootstrap access is 56 = 51 Read + 5 Execute over 26 Procedures, 6 Repos, 21 Stacks, 2 Actions and 1 Sync. The three new Execute grants are only the cohort roots; both Actions and all other new resources need Read. Check/propose Execute belongs to separately authorized operators, not the deployment bot. External reviewed bootstrap is required before the fail-closed relay can see the new declarations.
Metadata-only public-registry smoke checks passed for Docker Hub/Traefik, GHCR/Homepage and the custom Stirling registry, including manifest digest verification and bounded tag selection. No image layers, pin files, draft PRs or live deployments were created by those checks.
Integrated extension validation:
- Full Python CI-equivalent suite on Linux Python 3.13.17: 2,026 tests, 0 failures, 0 errors, 24 opt-in/tool-dependent skips. Node-backed Action tests and real Gitleaks boundary regressions ran. The disposable snapshot ran as UID 1001 with no network, host mounts, credentials or Docker socket during validation.
- Gitleaks 8.24.3 with full redaction: zero findings over the original PR history
and the new staged snapshot, including new files. Four synthetic fixture findings
are covered only by exact file-and-value
ANDexceptions forgeneric-api-key; regression tests preserve every other detector and reject broadened exceptions. - Fixed the CI-specific subprocess loader failure by retaining
LD_LIBRARY_PATHin the isolated Python fixture environment, not by exposing ambient credentials. - The first extension CI run exposed missing
nodeon the self-hosted runner PATH. Both Python/Komodo jobs now explicitly provision Node 22 with the pinned setup action; a regression test requires setup before Action tests rather than allowing silent skips. The corrected GitHub run must complete separately. - Updated older flat-graph/webhook/static-hash assertions to preserve meaningful dispatch, exclusion, schedule, credential-isolation and source-integrity contracts.
- Inline Action drift and resource validation passed: 26 Procedures, 2 Actions, 6 Repos and 21 Stacks. Changed workflow/Compose YAML checks passed.
- Markdown passed for 220 pages; strict MkDocs and source/built documentation coverage checks passed, including the new workflow's project-map entry.
- Independent security review's two findings were corrected: the Core dispatch token is outside the recursively chowned checkout, and Action bodies are imported inline rather than executed from runner-writable host files. Follow-up review identified no remaining high-confidence blocker.
These local results do not claim that the new PR head's GitHub checks or live acceptance have completed. Owner gates and safe credential handling are in the new runbook.
Execution graph¶
flowchart TD
R[Operator-reviewed explicit main dispatch] --> S[External Sync - wait for exact success]
S --> P[deploy-infra]
P --> I[infra-services chain]
P --> X[Phoenix pull then conditional deploy]
P --> Q[SonarQube pull then conditional deploy]
I --> H[Homelab pull - render and fingerprint]
H --> B[Conditional batch including Firefly]
B --> A[Independent application children]
A --> D[Reddit pull and conditional deploy]
A --> O[Orders pull and conditional deploy]
A --> G[PR Dashboard pull and conditional deploy]
A failed child does not stop parallel siblings, but still fails the parent. Preparation failures stop that host rather than deploying stale input. The relay lock does not serialize independent UI, scoped webhook or Ansible writers; drain those callers during general deployment and bootstrap.
Validation and remaining gates¶
Historical initial uplift validation, before the image-update extension above.
Integrated validation ran in the isolated feature/komodo-deployment-uplift
worktree based on 7e7f79b6f7cae45d98baf4e68196bcac55b18bdb:
- Resource validation passed: 21 Stacks, 6 Repos, 20 Procedures.
- The combined Komodo, relay, application, WS5 operating-contract, documentation and WS2 secret-rendering/procedure suites ran 456 tests successfully, with 2 opt-in checks skipped. Newer-main Firefly's default renderer backend and the owner-gated outbound-agent recipe remain protected by updated tests.
- All 4 safe-pull tests passed on Linux/Bash 5 in a disposable Python 3.13 container with the isolated checkout mounted read-only. No live host was used.
- Markdown fix/check passed for 219 pages;
mkdocs build --strictpassed;scripts/check-docs.py --site-dir sitepassed source and built-site checks. The original checkout's duplicate journal artifact was not copied into the PR. - Changed YAML and both Ansible task files passed lint; the existing runner-task line-length and lint-configuration notices remain warnings, not new defects.
- Ciphertext-only validation passed and the encrypted API source matches the owner's artifact byte-for-byte. No local decryption was performed.
git diff --checkpassed. Source-hash verification confirmed that the original implementation files were unchanged by packaging. No inventory, backup/restore implementation, canonical secret renderer, safe-pull implementation orPLAN.mdchanges were introduced.
The current-main LiteLLM renderer is used with required-key and URL-password validation and is included in the fingerprint. Integration did not revert the newer secret-rendering safeguards. PR CI and live application acceptance remain separate gates; local tests do not authorize merge or deployment.
Focused validation should cover input/secret mutations and unchanged selection, private-state failures, validated LiteLLM rendering, ownership-hook failures, exact Update identity, timeout/ambiguous outcomes, permission-filtered list responses, graph cycles, unsafe/disabled targets and host/application failure isolation. Run the existing operational-document contracts too; preserving them is part of integration, not an invitation to weaken their safety assertions. Safe-pull tests need Bash 4+ and Git. Source tests cannot substitute for live API execution or application acceptance.
See the rollout runbook for the three opt-in variables, pinned helper cache, reviewed external Sync, new-resource grants and first-run acceptance. Credential capture, dedicated-path migration, temporary LAN-identity preflight and persistent activation review/merge and the controlled runner apply are complete. Actual-runner read-only checks have now passed in both naturally occurring shared-parent states and after a full subsequent apply. External bootstrap and grants are now verified; first deployment and application acceptance remain pending. Do not dispatch until those gates pass. Native environment reviewer approval is absent, and no deployment is inferred from offline tests or version inspection.