carlos / platform Public

Sidecar isolation Task 2: the worker role at root, fenced like the run unit (D1, D19)

names.go gains RoleWorker and UnitPrefixWorker (carlos-instance-worker@),
with prefixFor, ParseRole and ParseUnitName cases; the slice name is
unchanged, a worker's slice being its own host's. The control service
admits worker on /start, /stop and /reset and sweeps the new template;
a worker start takes the run role's tier rule (always set-property: the
populated-slice exemption stays the legacy sidecar's), may name its staged
build, validated with validateStagedExe against the worker host's own app,
and is refused with a generation (one unit per host, never a generation).
clearLegacyClaimLaunch stays run-only.

The #292 ordered start/stop fence is keyed by (role, host) instead of host
and covers RoleRun and RoleWorker: a worker /stop cancels and joins the
worker starts in flight, applies the accepted-connection watermark to that
unit alone, and only then stops it, so a nested worker start that timed out
on the agent's side is never submitted by a late handler after the /stop
has answered (spec §3.5.1, D19). A worker's stop never fences the same
host's run unit, nor the reverse.

instance-exec and instance-exit accept worker (launch.worker.json,
exit.worker.<inv>.json); a worker "<iid>:<n>" is refused like a seed's.

Tests: TestParseUnitNameWorker, TestStartWorkerSetsTier,
TestStartWorkerRefusesGen, TestStopResetAndSweepCoverTheWorkerRole,
TestWorkerStartValidatesStagedExe, and the #292 shape parameterised by
role: TestWorkerStopCancelsAndJoinsHeldStart,
TestWorkerStartAcceptedBeforeStopIsRefused,
TestWorkerJoinPastBudgetAnswersError, TestWorkerStopDoesNotFenceRunUnit,
TestRunStopDoesNotFenceWorkerUnit; plus the trampoline's worker launch
and exit-record cases.

Census: two claim lines appended (server.go parseRequest, type Server);
census-propose is Task 12's.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 209b832524490a4778b43cb7dc04c66e3ec19401 parent fea6a05
13 files changed, +510 −115
  • implementation/census-claims-sidecar-isolation.txt +2 −0
  • internal/instancectl/legacystart.go +64 −46
  • internal/instancectl/legacystart_live_test.go +28 −10
  • internal/instancectl/legacystart_test.go +89 −8
  • internal/instancectl/names.go +16 −7
  • internal/instancectl/names_test.go +43 −3
  • internal/instancectl/server.go +50 −37
  • internal/instancectl/server_test.go +86 −0
  • internal/instancectl/stagedexe_linux_test.go +87 −0
  • internal/instanceexec/exec.go +2 −2
  • internal/instanceexec/exec_test.go +30 −0
  • internal/instanceexec/exit.go +2 −2
  • internal/instanceexec/exit_test.go +11 −0

CI failed — run details

CI log
=== gate ===
.amadan/test-gate.sh
=== launchctx (1 s)
ok  	amadan.net/carlos/platform.git/launchctx	0.013s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
=== vet (17 s)
=== gofmt (1 s)
gofmt -l .: clean
=== rest (171 s)
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	3.638s
ok  	github.com/carlosframework/platform/cmd/moveprobe	12.029s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.757s
ok  	github.com/carlosframework/platform/internal/activator	58.618s
ok  	github.com/carlosframework/platform/internal/agentsock	0.030s
ok  	github.com/carlosframework/platform/internal/alerting	0.007s
ok  	github.com/carlosframework/platform/internal/analytics	0.042s
ok  	github.com/carlosframework/platform/internal/appconfig	2.944s
ok  	github.com/carlosframework/platform/internal/apperr	0.227s
ok  	github.com/carlosframework/platform/internal/applog	0.286s
ok  	github.com/carlosframework/platform/internal/awsinv	0.007s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.010s
ok  	github.com/carlosframework/platform/internal/boxid	0.670s
?   	github.com/carlosframework/platform/internal/boxid/boxidtest	[no test files]
?   	github.com/carlosframework/platform/internal/boxid/internal/signtest	[no test files]
ok  	github.com/carlosframework/platform/internal/boxvolumes	0.009s
ok  	github.com/carlosframework/platform/internal/caps	0.017s
ok  	github.com/carlosframework/platform/internal/certwarm	0.008s
ok  	github.com/carlosframework/platform/internal/cgroup	0.797s
ok  	github.com/carlosframework/platform/internal/changelog	0.595s
ok  	github.com/carlosframework/platform/internal/claimactivation	1.307s
ok  	github.com/carlosframework/platform/internal/clamd	0.067s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
?   	github.com/carlosframework/platform/internal/controlname	[no test files]
?   	github.com/carlosframework/platform/internal/controlsock	[no test files]
ok  	github.com/carlosframework/platform/internal/dbdoor	7.954s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.021s
ok  	github.com/carlosframework/platform/internal/dbquery	0.005s
ok  	github.com/carlosframework/platform/internal/dbreads	2.130s
?   	github.com/carlosframework/platform/internal/dbreads/dbreadstest	[no test files]
ok  	github.com/carlosframework/platform/internal/dbverify	0.008s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.004s
ok  	github.com/carlosframework/platform/internal/docsite	0.009s
ok  	github.com/carlosframework/platform/internal/domains	2.975s
ok  	github.com/carlosframework/platform/internal/doorkey	0.008s
ok  	github.com/carlosframework/platform/internal/economics	0.009s
ok  	github.com/carlosframework/platform/internal/edge	45.450s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.008s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.135s
ok  	github.com/carlosframework/platform/internal/gate	1.655s
ok  	github.com/carlosframework/platform/internal/genselect	0.017s
ok  	github.com/carlosframework/platform/internal/geo	0.020s
ok  	github.com/carlosframework/platform/internal/geoip	0.029s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.153s
ok  	github.com/carlosframework/platform/internal/host	142.632s
ok  	github.com/carlosframework/platform/internal/hostactors	6.440s
ok  	github.com/carlosframework/platform/internal/hostactors/awsmembership	0.006s
ok  	github.com/carlosframework/platform/internal/hostbox	2.856s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.755s
ok  	github.com/carlosframework/platform/internal/hostenv	0.009s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.006s
ok  	github.com/carlosframework/platform/internal/hostexec	5.189s
ok  	github.com/carlosframework/platform/internal/hostfence	0.055s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.256s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.018s
ok  	github.com/carlosframework/platform/internal/hostguard	1.047s
ok  	github.com/carlosframework/platform/internal/hostops	1.943s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.005s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.005s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.597s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.231s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.005s
ok  	github.com/carlosframework/platform/internal/infraview	0.005s
ok  	github.com/carlosframework/platform/internal/instancectl	6.898s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.809s
ok  	github.com/carlosframework/platform/internal/instancefs	0.020s
ok  	github.com/carlosframework/platform/internal/instancekey	0.005s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.008s
ok  	github.com/carlosframework/platform/internal/layout	1.783s
ok  	github.com/carlosframework/platform/internal/lbhealth	0.010s
ok  	github.com/carlosframework/platform/internal/lease	10.277s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.529s
ok  	github.com/carlosframework/platform/internal/ledger	0.033s
ok  	github.com/carlosframework/platform/internal/mail	0.224s
ok  	github.com/carlosframework/platform/internal/maintenance	0.079s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.567s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.107s
ok  	github.com/carlosframework/platform/internal/metrics	0.011s
ok  	github.com/carlosframework/platform/internal/moves	22.336s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.996s
ok  	github.com/carlosframework/platform/internal/objstore	0.306s
ok  	github.com/carlosframework/platform/internal/ops	0.262s
ok  	github.com/carlosframework/platform/internal/park	0.273s
ok  	github.com/carlosframework/platform/internal/plans	0.009s
ok  	github.com/carlosframework/platform/internal/privatefile	0.007s
2026/10/24 20:55:52 provision: listing apps/bab/blog/instances/: list refused — its records are unseen this pass
2026/10/24 20:55:52 provision: listing apps/bab/: listdirs refused — its records are unseen this pass
2026/10/24 20:55:52 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:52 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:52 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:52 provision: listing apps/bac/tito/instances/: list refused — its records are unseen this pass
2026/10/24 20:55:52 provision: listing apps/bac/tito/instances/: list refused — its records are unseen this pass
2026/10/24 20:55:52 provision: skipping record apps/bac/tito/instances/one.example.com.json: unreadable: read refused
2026/10/24 20:55:52 provision: listing apps/bac/tito/instances/: list refused — its records are unseen this pass
2026/10/24 20:55:52 provision: an app's records could not be listed: they are skipped
2026/10/24 20:55:52 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:55:52 provision: remove retired instance route shop.example: provision: claim admission unavailable
2026/10/24 20:55:52 provision: remove retired instance route shop.example: provision: claim admission unavailable
2026/10/24 20:55:52 provision: remove retired sidecar route worker.sidecar.internal: provision: claim admission unavailable
2026/10/24 20:55:52 provision: remove retired sidecar route worker.sidecar.internal: provision: claim admission unavailable
2026/10/24 20:55:52 provision: claim install acme.go.tito.io: provision: claim admission unavailable
2026/10/24 20:55:52 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:52 provision: claim install acme.go.tito.io: registry: claim row ownership changed or unknown
2026/10/24 20:55:52 provision: claim install bot.eleven.bac.sidecar.internal: provision: claim admission unavailable
2026/10/24 20:55:52 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/24 20:55:52 provision: claim install bot.eleven.bac.sidecar.internal: registry: claim row ownership changed or unknown
2026/10/24 20:55:52 provision: refusing to mint route acme.go.tito.io (app "tito" acct "bac"): this box declares CARLOS_INBOUND=none (no-inbound) and cannot serve routed instance traffic
2026/10/24 20:55:52 provision: acme.go.tito.io: not minting this pass: the box's layout intent could not be read: no sealed config
2026/10/24 20:55:52 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:52 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:52 provision: REFUSING to mint sidecar bot.eleven.bac.sidecar.internal: bot.eleven.bac.sidecar.internal-sidecar.env is staged on this box — the legacy file co-spawn and a native sidecar row are two mechanisms for one host; remove the file to migrate this host onto sidecar records
2026/10/24 20:55:52 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/24 20:55:52 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/24 20:55:53 provision: refusing to mint route acme.go.tito.io (app "tito" acct "bac"): this box declares CARLOS_INBOUND=none (no-inbound) and cannot serve routed instance traffic
2026/10/24 20:55:53 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: REFUSING to mint sidecar bot.eleven.bac.sidecar.internal: bot.eleven.bac.sidecar.internal-sidecar.env is staged on this box — the legacy file co-spawn and a native sidecar row are two mechanisms for one host; remove the file to migrate this host onto sidecar records
2026/10/24 20:55:53 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/24 20:55:53 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/24 20:55:53 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: claim install acme.go.tito.io: registry: claim row ownership changed or unknown
2026/10/24 20:55:53 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: claim install acme.go.tito.io: registry: claim row ownership changed or unknown
2026/10/24 20:55:53 provision: bot.eleven.bac.sidecar.internal exists but is not a sidecar row this pass owns (kind "sidecar" provisioned false app "eleven" acct "bac") — leaving it alone
2026/10/24 20:55:53 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/24 20:55:53 provision: claim install bot.eleven.bac.sidecar.internal: registry: claim row ownership changed or unknown
2026/10/24 20:55:53 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/24 20:55:53 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/24 20:55:53 provision: bot.eleven.bac.sidecar.internal exists but is not a sidecar row this pass owns (kind "sidecar" provisioned false app "eleven" acct "bac") — leaving it alone
2026/10/24 20:55:53 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:55:53 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: skipping record apps/bac/tito/instances/acme.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:53 provision: skipping record apps/bac/tito/instances/new.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:53 provision: not removing acme.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/24 20:55:53 provision: skipping record apps/bac/tito/instances/acme.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:53 provision: skipping record apps/bac/tito/instances/new.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:53 provision: minted instance route rb.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rc.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rd.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route re.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rf.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rg.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rh.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route ri.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route ra.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rj.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rk.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rl.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rc.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rd.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route ra.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route rb.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: skipping record apps/bac/tito/instances/rb.go.tito.io.json: read failed: read refused
2026/10/24 20:55:53 provision: skipping record apps/bac/tito/instances/rb.go.tito.io.json: read failed: read refused
2026/10/24 20:55:53 provision: minted instance route re.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.appg.example (app "appg" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.apph.example (app "apph" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.appi.example (app "appi" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.appj.example (app "appj" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.appe.example (app "appe" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.appf.example (app "appf" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.appa.example (app "appa" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.appb.example (app "appb" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.appc.example (app "appc" acct "bac")
2026/10/24 20:55:53 provision: minted instance route x.appd.example (app "appd" acct "bac")
2026/10/24 20:55:53 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/24 20:55:53 provision: minted instance route r0080.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0123.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0079.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0099.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0122.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0003.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0025.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0042.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0044.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0094.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0114.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0116.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0121.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0023.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0026.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0107.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0006.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0040.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0060.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0095.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0037.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0050.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0090.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0096.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0117.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0028.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0072.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0101.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0032.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0039.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0046.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0105.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0111.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0049.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0055.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0057.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0061.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0092.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0119.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0127.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0033.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0069.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0103.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0004.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0030.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0074.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0075.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0043.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0059.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0082.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0047.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0064.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0066.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0081.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0098.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0106.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0016.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0067.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0102.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0104.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0113.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0007.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0009.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0091.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0115.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0128.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0112.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0014.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0073.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0076.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0118.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0029.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0041.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0062.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0129.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0013.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0020.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0078.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0093.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0097.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0008.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0038.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0056.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0100.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0109.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0002.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0010.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0017.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0035.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0063.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0068.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0087.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0012.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0045.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0001.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0084.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0124.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0018.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0024.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0027.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0031.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0051.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0011.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0070.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0022.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0036.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0120.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0000.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0019.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0052.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0058.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0015.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0021.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0053.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0083.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0086.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0089.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0110.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0125.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0054.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0071.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0085.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0088.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0126.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0005.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0065.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0108.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0034.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0048.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:53 provision: minted instance route r0077.go.tito.io (app "tito" acct "bac")
--- FAIL: TestPlan9B8RecordWriteCensus (0.74s)
    plan9_b8_census_test.go:372: record-write census rejected 2 site(s); update the explicit inventory only after reviewing the source:
          changed site internal/instanceexec/exit.go:RunExit: source differs from the reviewed digest
          changed site internal/ops/litestream.go:FleetDBsWithSkipped: source differs from the reviewed digest
2026/10/24 20:55:54 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: refusing to mint route acme.go.tito.io (app "tito" acct "bac"): this box declares CARLOS_INBOUND=none (no-inbound) and cannot serve routed instance traffic
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route console.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted service route ha.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted service route svc.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route whole.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route shared.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: converged acme.go.tito.io channel stable -> edge
2026/10/24 20:55:55 provision: converged acme.go.tito.io channel edge -> canary/rehearsal
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: skipping record apps/bac/tito/instances/acme.go.tito.io.json: empty channel
2026/10/24 20:55:55 provision: not removing acme.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: converged acme.go.tito.io environment "" -> "staging"
2026/10/24 20:55:55 provision: converged acme.go.tito.io environment "staging" -> ""
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: converged acme.go.tito.io steering "" -> "latency"
2026/10/24 20:55:55 provision: converged acme.go.tito.io steering "latency" -> ""
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: converged acme.go.tito.io hibernate false -> true
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: converged acme.go.tito.io hibernate true -> false (replication "daily" cleared: only a hibernating route's rung is read)
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: converged acme.go.tito.io status-named false -> true
2026/10/24 20:55:55 provision: converged acme.go.tito.io status-named true -> false
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted service route ha.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: converged ha.go.tito.io upstreams addr "" -> "", addrs "127.0.0.1:7001,127.0.0.1:7002" -> "127.0.0.1:7001,127.0.0.1:7003" (probe "{\"path\":\"/\",\"status\":200,\"interval_ms\":2000,\"fail_after\":3,\"rise_after\":2}" -> "{\"path\":\"/\",\"status\":200,\"interval_ms\":2000,\"fail_after\":3,\"rise_after\":2}")
2026/10/24 20:55:55 provision: converged ha.go.tito.io upstreams addr "" -> "", addrs "127.0.0.1:7001,127.0.0.1:7003" -> "127.0.0.1:7003,127.0.0.1:7001" (probe "{\"path\":\"/\",\"status\":200,\"interval_ms\":2000,\"fail_after\":3,\"rise_after\":2}" -> "{\"path\":\"/\",\"status\":200,\"interval_ms\":2000,\"fail_after\":3,\"rise_after\":2}")
2026/10/24 20:55:55 provision: converged ha.go.tito.io upstreams addr "" -> "", addrs "127.0.0.1:7003,127.0.0.1:7001" -> "127.0.0.1:7003,127.0.0.1:7001" (probe "{\"path\":\"/\",\"status\":200,\"interval_ms\":2000,\"fail_after\":3,\"rise_after\":2}" -> "{\"path\":\"/healthz\",\"status\":200,\"interval_ms\":2000,\"fail_after\":3,\"rise_after\":2}")
2026/10/24 20:55:55 provision: minted service route whole.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: converged whole.go.tito.io upstreams addr "127.0.0.1:9443" -> "", addrs "" -> "127.0.0.1:9443,127.0.0.1:9444" (probe "" -> "{\"path\":\"/\",\"status\":200,\"interval_ms\":2000,\"fail_after\":3,\"rise_after\":2}")
2026/10/24 20:55:55 provision: converged whole.go.tito.io upstreams addr "" -> "", addrs "127.0.0.1:9443,127.0.0.1:9444" -> "" (probe "{\"path\":\"/\",\"status\":200,\"interval_ms\":2000,\"fail_after\":3,\"rise_after\":2}" -> "")
2026/10/24 20:55:55 provision: whole.go.tito.io: record describes a different route SHAPE than the row (kind "instance"/"service" unit ""/"" socket "/run/carlos/whole.go.tito.io.sock"/"" db "/data/whole.go.tito.io.db"/"") — not converged in place; `carlos remove whole.go.tito.io` purges this route's capability grants (registry.Delete's contract, not a bug) before the next pass re-mints it, so treat that as a last resort, not a routine fix
2026/10/24 20:55:55 provision: minted service route whole.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: removed retired instance route acme.go.tito.io (record gone)
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: skipping accounts/bac/account.json: malformed account manifest: invalid character 'o' in literal null (expecting 'u')
2026/10/24 20:55:55 provision: removed retired instance route acme.go.tito.io (record gone)
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: manifest bac/tito: read failed: provision: manifest bac/tito: invalid character 'o' in literal null (expecting 'u')
2026/10/24 20:55:55 provision: skipping record apps/bac/tito/instances/acme.go.tito.io.json: disagrees with its own path (host="wrong-host.go.tito.io" account="bac" app="tito")
2026/10/24 20:55:55 provision: skipping record apps/bac/tito/instances/acme$evil.go.tito.io.json: host "acme$evil.go.tito.io" is not a hostname
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: skipping record apps/bac/tito/instances/acme$evil.go.tito.io.json: host "acme$evil.go.tito.io" is not a hostname
2026/10/24 20:55:55 provision: not removing acme$evil.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/24 20:55:55 provision: skipping record apps/bac/tito/instances/bad-service.go.tito.io.json: invalid shape: kind service with no addr: the platform only proxies to a service, so the record must name the upstream it proxies to
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: skipping record apps/bac/tito/instances/bad-service.go.tito.io.json: invalid shape: kind service with no addr: the platform only proxies to a service, so the record must name the upstream it proxies to
2026/10/24 20:55:55 provision: not removing bad-service.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/24 20:55:55 provision: skipping record apps/bac/tito/instances/no-channel.go.tito.io.json: empty channel
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: skipping record apps/bac/tito/instances/no-channel.go.tito.io.json: empty channel
2026/10/24 20:55:55 provision: not removing no-channel.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: skipping record apps/bac/tito/instances/acme.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:55 provision: not removing acme.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/24 20:55:55 provision: removed retired instance route ghost.go.tito.io (record gone)
2026/10/24 20:55:55 provision: duplicate host dup.go.tito.io: keeping apps/bac/tito/instances/dup.go.tito.io.json, skipping apps/bad/tito/instances/dup.go.tito.io.json
2026/10/24 20:55:55 provision: minted instance route dup.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/24 20:55:55 provision: removed retired instance route acme.go.tito.io (app in the trash)
2026/10/24 20:55:55 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/24 20:55:55 provision: removed retired static route site.go.tito.io (app in the trash)
2026/10/24 20:55:55 provision: trash marker for bac/tito unreadable: release: malformed trash marker for bac/tito: invalid character 'n' looking for beginning of object key string — not minting for this app
2026/10/24 20:55:55 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/24 20:55:55 provision: removed retired instance route acme.go.tito.io (app in the trash)
2026/10/24 20:55:55 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/24 20:55:55 provision: trash marker for bac/tito unreadable: release: malformed trash marker for bac/tito: invalid character 'n' looking for beginning of object key string — not minting for this app
2026/10/24 20:55:55 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/24 20:55:55 provision: converged acme.go.tito.io channel stable -> edge
2026/10/24 20:55:55 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/24 20:55:55 provision: converged acme.go.tito.io channel canary/rehearsal -> edge
2026/10/24 20:55:55 provision: converged acme.go.tito.io environment "staging" -> ""
2026/10/24 20:55:55 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/24 20:55:55 provision: acme.go.tito.io exists but is not the route this record describes (backing "unit" (box-scoped, not expressible in a record)) — leaving it alone
2026/10/24 20:55:55 provision: acme.go.tito.io exists but is not the route this record describes (tunnel "pi-fleet" (box-scoped, not expressible in a record)) — leaving it alone
2026/10/24 20:55:55 provision: acme.go.tito.io exists but is not the route this record describes (caps "port:25" (box-scoped, not expressible in a record)) — leaving it alone
2026/10/24 20:55:55 provision: acme.go.tito.io exists but is not the route this record describes (platform (owned by the domains reconcile pass)) — leaving it alone
2026/10/24 20:55:55 provision: acme.go.tito.io exists but is not the route this record describes (canary (owned by the domains reconcile pass)) — leaving it alone
2026/10/24 20:55:55 provision: acme.go.tito.io exists but is not the route this record describes (db "/data/other.db"/"/data/acme.go.tito.io.db") — leaving it alone
2026/10/24 20:55:55 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/24 20:55:55 provision: removed retired instance route acme.go.tito.io (record gone)
2026/10/24 20:55:55 provision: dry-run: would mint instance route acme.go.tito.io (app "tito" acct "bac" channel "stable")
2026/10/24 20:55:55 provision: dry-run: would adopt existing route adopt.go.tito.io (app "tito" acct "bac") — this record would own its channel and environment
2026/10/24 20:55:55 provision: dry-run: would converge adopt.go.tito.io channel stable -> edge
2026/10/24 20:55:55 provision: dry-run: would converge adopt.go.tito.io environment "" -> "staging"
2026/10/24 20:55:55 provision: dry-run: would converge owned.go.tito.io channel stable -> edge
2026/10/24 20:55:55 provision: dry-run: would converge owned.go.tito.io environment "" -> "staging"
2026/10/24 20:55:55 provision: dry-run: would converge owned.go.tito.io hibernate false -> true
2026/10/24 20:55:55 provision: dry-run: would converge owned.go.tito.io seed
2026/10/24 20:55:55 provision: dry-run: would remove retired instance route gone.go.tito.io (record gone)
2026/10/24 20:55:55 provision: adopted existing route adopt.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/24 20:55:55 provision: converged adopt.go.tito.io channel stable -> edge
2026/10/24 20:55:55 provision: converged adopt.go.tito.io environment "" -> "staging"
2026/10/24 20:55:55 provision: converged owned.go.tito.io channel stable -> edge
2026/10/24 20:55:55 provision: converged owned.go.tito.io environment "" -> "staging"
2026/10/24 20:55:55 provision: converged owned.go.tito.io hibernate false -> true
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: removed retired instance route gone.go.tito.io (record gone)
2026/10/24 20:55:55 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: adopted existing route story.carlosframework.com (app "story" acct "bad") — this record now owns its channel and environment
2026/10/24 20:55:55 provision: story.carlosframework.com exists but is not the route this record describes (kind "instance"/"static") — leaving it alone
2026/10/24 20:55:55 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted service route pubs.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted service route pub.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route exec.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted service route loop.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted service route named.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted service route priv.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted instance route unit.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:55 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted static route pisite.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: removed retired static route site.go.tito.io (record gone)
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route acme-uk.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: converged acme.go.tito.io replicas "ie,uk" -> "uk"
2026/10/24 20:55:56 provision: converged acme.go.tito.io replicas "uk" -> ""
2026/10/24 20:55:56 provision: minted instance route acme-uk.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: converged acme-uk.go.tito.io replica mark true -> false
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route router.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: acme.go.tito.io: row is on the nested layout (db "/data/instances/acme.go.tito.io/acme.go.tito.io.db" socket "/run/carlos/instances/acme.go.tito.io/app.sock"); the record's shared paths are not drift
2026/10/24 20:55:56 provision: minted instance route router.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route router.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: minted instance route router.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:56 provision: router.go.tito.io: not minting this pass: the box's layout intent could not be read: store down
2026/10/24 20:55:56 provision: acme.go.tito.io: not minting this pass: the box's layout intent could not be read: store down
2026/10/24 20:55:56 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/24 20:55:56 provision: converged acme.go.tito.io channel stable -> edge
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io held landing by mv_acme (app "tito" acct "bac")
2026/10/24 20:55:56 provision: not removing acme.go.tito.io (record gone): participant of a running operation
2026/10/24 20:55:56 provision: removed retired instance route acme.go.tito.io (record gone)
2026/10/24 20:55:56 provision: minted instance route acme.go.tito.io held landing by mv_acme (app "tito" acct "bac")
2026/10/24 20:55:56 provision: refusing to mint held route acme.go.tito.io (app "tito" acct "bac"): this box declares CARLOS_INBOUND=none (no-inbound) and cannot serve routed instance traffic
2026/10/24 20:55:56 provision: held acme.go.tito.io landing by mv_acme (was ready)
2026/10/24 20:55:56 provision: acme.go.tito.io: held restoring by rb_other, not by mv_acme; leaving it
2026/10/24 20:55:56 provision: acme.go.tito.io: the row is not this pass's (app "other" acct ""); not holding it for mv_acme
2026/10/24 20:55:56 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/24 20:55:56 provision: dry-run: would mint instance route acme.go.tito.io held landing by mv_acme (app "tito" acct "bac" channel "stable")
2026/10/24 20:55:56 provision: acme.go.tito.io: not held landing by mv_acme: the row's hold changed under this pass; the next pass judges it afresh
2026/10/24 20:55:56 provision: held acme.go.tito.io landing by mv_acme (was ready)
2026/10/24 20:55:56 provision: minted instance route acme.example.com (app "shop" acct "bac")
2026/10/24 20:55:56 provision: minted instance route plain.example.com (app "shop" acct "bac")
2026/10/24 20:55:56 provision: skipping record apps/bac/shop/instances/garbled.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:56 provision: skipping record apps/bac/shop/instances/liar.shop.example.com.json: disagrees with its own path (host="other.shop.example.com" account="bac" app="shop")
2026/10/24 20:55:56 provision: skipping record apps/bac/shop/instances/garbled.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:56 provision: skipping record apps/bac/shop/instances/liar.shop.example.com.json: disagrees with its own path (host="other.shop.example.com" account="bac" app="shop")
2026/10/24 20:55:56 provision: skipping record apps/bac/shop/instances/garbled.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:56 provision: skipping record apps/bac/shop/instances/liar.shop.example.com.json: disagrees with its own path (host="other.shop.example.com" account="bac" app="shop")
2026/10/24 20:55:56 provision: skipping record apps/bac/shop/instances/garbled.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:56 provision: listing apps/bac/shop/instances/: list refused — its records are unseen this pass
2026/10/24 20:55:57 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:57 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:57 provision: removed retired instance route acme.go.tito.io (record gone)
2026/10/24 20:55:57 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:57 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/24 20:55:57 provision: minted sidecar route bot.shop.bac.sidecar.internal (app "shop" acct "bac" args ["run"])
2026/10/24 20:55:57 provision: removed retired sidecar route bot.shop.bac.sidecar.internal (record gone)
2026/10/24 20:55:57 provision: minted sidecar route bot.shop.bum.sidecar.internal (app "shop" acct "bum" args ["run"])
2026/10/24 20:55:57 provision: minted sidecar route bot.shop.bum.sidecar.internal (app "shop" acct "bum" args ["run"])
2026/10/24 20:55:57 provision: sidecar bot.shop.bum.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:55:57 provision: sidecar bot.shop.bum.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:55:57 provision: sidecar bot.shop.bum.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:55:57 provision: removed retired sidecar route bot.shop.bum.sidecar.internal (record gone)
2026/10/24 20:55:57 provision: sidecar bot.shop.bum.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:55:57 provision: removed retired sidecar route bot.shop.bum.sidecar.internal (record gone)
2026/10/24 20:55:57 provision: a.shop.example.com: swapped for import sd_juarxms4x2lgx4jzqyj5nih2du (the original route is journaled)
2026/10/24 20:55:57 provision: a.shop.example.com: import sd_juarxms4x2lgx4jzqyj5nih2du committed: its gate is cleared and its journal row dropped
2026/10/24 20:55:57 provision: a.shop.example.com: row is on the nested layout (db "/data/instances/a.shop.example.com/a.shop.example.com.db" socket "/run/carlos/instances/a.shop.example.com/app.sock"); the record's shared paths are not drift
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_etkjs4be7jervzmr2o72tzveaa rolled back to its original route
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_etkjs4be7jervzmr2o72tzveaa: the host's replica is left alone: the record apps/bac/shop/instances/a.shop.example.com.json holds the host
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_etkjs4be7jervzmr2o72tzveaa is cleaned up and its journal row retired
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_ypuslmktc725qtg7agvuohemci rolled back to its original route
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_ypuslmktc725qtg7agvuohemci: the host's replica is left alone: the record apps/bac/shop/instances/a.shop.example.com.json holds the host
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_ypuslmktc725qtg7agvuohemci is cleaned up and its journal row retired
2026/10/24 20:55:58 provision: dry-run: would redo a.shop.example.com's swap record CAS for import sd_4obylbvyl5fqtwemla6a6twfxi
2026/10/24 20:55:58 provision: dry-run: would clear a.shop.example.com's import gate
2026/10/24 20:55:58 provision: dry-run: would converge a.shop.example.com seed source
2026/10/24 20:55:58 provision: a.shop.example.com: row is on the nested layout (db "/data/instances/a.shop.example.com/a.shop.example.com.db" socket "/run/carlos/instances/a.shop.example.com/app.sock"); the record's shared paths are not drift
2026/10/24 20:55:58 provision: a.shop.example.com: the import's record apps/bac/shop/instances/a.shop.example.com.json is not one this pass can use (connection reset)
2026/10/24 20:55:58 provision: a.shop.example.com: the import's record apps/bac/shop/instances/a.shop.example.com.json is not one this pass can use (provision: invalid maintenance projection or protected fields)
2026/10/24 20:55:58 provision: a.shop.example.com: the import's record apps/bac/shop/instances/a.shop.example.com.json is not one this pass can use (the record under apps/bac/shop/instances/a.shop.example.com.json is apps/bac/shop/instances/b.shop.example.com.json's)
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_skpuecg5xhrx723mzyfgrplngm cannot roll back on this box: the activator is not wired; it stays journaled and gated
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_e3gbljgtjaeyi5zsrudluyf6ja cannot roll back on this box: the activator is not wired; it stays journaled and gated
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_fsnsxbtouniogqdbv4ajtjcoxy is journaled, but this box cannot act on imports; its row is left alone
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_yi6n4glu2h7q5x6kz56tz3zqye is journaled, but this box cannot act on imports; its row is left alone
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_ggdq4jfvymwnvuujb46ocx2qhu is journaled, but this box cannot act on imports; its row is left alone
2026/10/24 20:55:58 provision: the import journal could not be read: registry: import journal a.shop.example.com: original row: unexpected end of JSON input (no import is acted on and no route is removed this pass)
2026/10/24 20:55:58 provision: b.shop.example.com: import sd_nw75ghuirx2xku46jpvcu2de5i left alone this pass: the import journal could not be read
2026/10/24 20:55:58 provision: c.shop.example.com: import sd_dkxnpasx6vtjeggqxm4rkd4v6y left alone this pass: the import journal could not be read
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_kim6fofehbhzxtc34klafii4di committed: its gate is cleared and its journal row dropped
2026/10/24 20:55:58 provision: a.shop.example.com: not converging import sd_77nusbhexh5l4zuqsl2atsu47m: it is swapped but not committed, and the row on this box has no import gate
2026/10/24 20:55:58 provision: a.shop.example.com: not converging import sd_aewchxnewn2gdmq357soif372a: it is swapped but not committed, and the row on this box has no import gate
2026/10/24 20:55:58 provision: a.shop.example.com: not converging import sd_7phbawoegb2uossf5sojyjudca: it is placed but not committed, and the row on this box has no import gate
2026/10/24 20:55:58 provision: a.shop.example.com: not converging import sd_zcq26cwyhodtkwxti7ubgb2mue: it is placed but not committed, and the row on this box has no import gate
2026/10/24 20:55:58 provision: a.shop.example.com: row is on the nested layout (db "/data/instances/a.shop.example.com/a.shop.example.com.db" socket "/run/carlos/instances/a.shop.example.com/app.sock"); the record's shared paths are not drift
2026/10/24 20:55:58 provision: converge seed source on a.shop.example.com: constraint failed: seed_source is read-only here (1811)
2026/10/24 20:55:58 provision: a.shop.example.com: row is on the nested layout (db "/data/instances/a.shop.example.com/a.shop.example.com.db" socket "/run/carlos/instances/a.shop.example.com/app.sock"); the record's shared paths are not drift
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_3kclquss7ygwftp4jzbfgyxnb4 committed: its gate is cleared and its journal row dropped
2026/10/24 20:55:58 provision: a.shop.example.com: row is on the nested layout (db "/data/instances/a.shop.example.com/a.shop.example.com.db" socket "/run/carlos/instances/a.shop.example.com/app.sock"); the record's shared paths are not drift
2026/10/24 20:55:58 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:58 provision: acme.go.tito.io: row is on the nested layout (db "/data/instances/acme.go.tito.io/acme.go.tito.io.db" socket "/run/carlos/instances/acme.go.tito.io/app.sock"); the record's shared paths are not drift
2026/10/24 20:55:58 provision: acme.go.tito.io: not minting a seeded instance: this box does not mint nested (<nil>)
2026/10/24 20:55:58 provision: acme.go.tito.io: not minting a seeded instance: claim control is on for this box
2026/10/24 20:55:58 provision: acme.go.tito.io: aborted seed sd_yp2o3dzmbtuwnrrjvcsu6iwmrm: this box cannot read seed markers, and places no seed; nothing local is removed
2026/10/24 20:55:58 provision: acme.go.tito.io: aborted seed sd_yp2o3dzmbtuwnrrjvcsu6iwmrm rolled back (no box had swapped it)
2026/10/24 20:55:58 provision: acme.go.tito.io: not minting a seeded instance: its record does not describe a hibernating instance
2026/10/24 20:55:58 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:58 provision: removed instance route acme.go.tito.io (its seed sd_pkvgj5ackc2xdcfi42hmqwq2we was aborted); the next pass rolls it back
2026/10/24 20:55:58 provision: acme.go.tito.io: aborted seed sd_pkvgj5ackc2xdcfi42hmqwq2we: this box cannot read seed markers, and places no seed; nothing local is removed
2026/10/24 20:55:58 provision: acme.go.tito.io: aborted seed sd_pkvgj5ackc2xdcfi42hmqwq2we rolled back (no box had swapped it)
2026/10/24 20:55:58 provision: acme.go.tito.io: aborted seed sd_ffwpxs7dj4xj2avxucvxpee4vu has a row this pass may not remove; leaving the seed aborted
2026/10/24 20:55:58 provision: acme.go.tito.io: aborted seed sd_7kugwzy7he3s2xdr57mua6aktu rolled back (no box had swapped it)
2026/10/24 20:55:58 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:58 provision: removed instance route acme.go.tito.io (its seed sd_rhlhrvcwhn7gjrsxkvm4xefikq was aborted); the next pass rolls it back
2026/10/24 20:55:58 provision: acme.go.tito.io: aborted seed sd_rhlhrvcwhn7gjrsxkvm4xefikq has no route on this box: its local data is removed
2026/10/24 20:55:58 provision: acme.go.tito.io: aborted seed sd_rhlhrvcwhn7gjrsxkvm4xefikq rolled back (no box had swapped it)
2026/10/24 20:55:58 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:55:58 provision: a.shop.example.com: swapped for import sd_dcy2h2w5w3z747gypiphi6tgxq (the original route is journaled)
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_dcy2h2w5w3z747gypiphi6tgxq rolled back to its original route
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_dcy2h2w5w3z747gypiphi6tgxq is cleaned up and its journal row retired
2026/10/24 20:55:58 provision: a.shop.example.com: swapped for import sd_kufmnu22w4cove7qa6ufjjzlu4 (the original route is journaled)
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_kufmnu22w4cove7qa6ufjjzlu4 rolled back to its original route
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_kufmnu22w4cove7qa6ufjjzlu4 is cleaned up and its journal row retired
2026/10/24 20:55:58 provision: a.shop.example.com: swapped for import sd_rgp5i5sc7f2msn2pghic2drari (the original route is journaled)
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_rgp5i5sc7f2msn2pghic2drari rolled back to its original route
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_rgp5i5sc7f2msn2pghic2drari: the host's replica is left alone: its record no longer names this seed
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_rgp5i5sc7f2msn2pghic2drari is cleaned up and its journal row retired
2026/10/24 20:55:58 provision: a.shop.example.com: swapped for import sd_hpmjwy2hzuo74iukj6ocmfmtau (the original route is journaled)
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_hpmjwy2hzuo74iukj6ocmfmtau rollback deferred: the host is not fenced and stopped yet (<nil>)
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_hpmjwy2hzuo74iukj6ocmfmtau rolled back to its original route
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_hpmjwy2hzuo74iukj6ocmfmtau is cleaned up and its journal row retired
2026/10/24 20:55:58 provision: a.shop.example.com: swapped for import sd_addm7zhrzzzaye5ghzpuykonl4 (the original route is journaled)
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_addm7zhrzzzaye5ghzpuykonl4 rollback deferred: the host is not fenced and stopped yet (stop unproven)
2026/10/24 20:55:58 provision: a.shop.example.com: swapped for import sd_dsihevxvlmqdevjjkxkbfp2ani (the original route is journaled)
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_dsihevxvlmqdevjjkxkbfp2ani is cleaned up and its journal row retired
2026/10/24 20:55:58 provision: a.shop.example.com: swapped for import sd_oftsl4yd6cu4mpx5tlyu5s4dbq (the original route is journaled)
2026/10/24 20:55:58 provision: skipping record apps/bac/shop/instances/a.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_oftsl4yd6cu4mpx5tlyu5s4dbq left alone this pass: its record could not be read
2026/10/24 20:55:58 provision: a.shop.example.com: swapped for import sd_rx7zmxycrapzcuribkkjh3z52y (the original route is journaled)
2026/10/24 20:55:58 provision: skipping record apps/bac/shop/instances/a.shop.example.com.json: read failed: connection reset
2026/10/24 20:55:58 provision: a.shop.example.com: import sd_rx7zmxycrapzcuribkkjh3z52y left alone this pass: its record could not be read
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_bofwbmdyr63mcebzm5xjwxrz2u (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_bofwbmdyr63mcebzm5xjwxrz2u: close the import gate: constraint failed: injected (1811)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_bofwbmdyr63mcebzm5xjwxrz2u rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_bofwbmdyr63mcebzm5xjwxrz2u is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_ylfbwbodkzilmd6bl7qdsaf7nm (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_ylfbwbodkzilmd6bl7qdsaf7nm: close the import gate: constraint failed: injected (1811)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_ylfbwbodkzilmd6bl7qdsaf7nm rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_ylfbwbodkzilmd6bl7qdsaf7nm is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_rj3nfbthjwyvnm5yjk6twne2qm (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_rj3nfbthjwyvnm5yjk6twne2qm rollback deferred: the host is not fenced and stopped yet (injected failure at fence)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_rj3nfbthjwyvnm5yjk6twne2qm rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_rj3nfbthjwyvnm5yjk6twne2qm is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_i2r7nyzndt4bea7gsx6mrm35ei (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_i2r7nyzndt4bea7gsx6mrm35ei rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_i2r7nyzndt4bea7gsx6mrm35ei is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_kzvvkgszuzaidsry53ijc75q6u (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_kzvvkgszuzaidsry53ijc75q6u: retire its instance key: injected failure at retire-key
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_kzvvkgszuzaidsry53ijc75q6u rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_kzvvkgszuzaidsry53ijc75q6u is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_gbbpyvnq6ijydev6vyshizkuui (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_gbbpyvnq6ijydev6vyshizkuui rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_gbbpyvnq6ijydev6vyshizkuui is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_sstalqlnipjgs7hqozzgixvliu (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_sstalqlnipjgs7hqozzgixvliu: remove its local data: injected failure at remove-local
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_sstalqlnipjgs7hqozzgixvliu rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_sstalqlnipjgs7hqozzgixvliu is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_x3slvipsynzmxgo7fdirkj2jnu (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_x3slvipsynzmxgo7fdirkj2jnu rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_x3slvipsynzmxgo7fdirkj2jnu is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_qluffcltddo37telifggyhw5wm (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_qluffcltddo37telifggyhw5wm: remove its local data: injected failure at remove-local-done
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_qluffcltddo37telifggyhw5wm rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_qluffcltddo37telifggyhw5wm is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_dsvyomlcubehlrbewg3dyocjpu (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_dsvyomlcubehlrbewg3dyocjpu rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_dsvyomlcubehlrbewg3dyocjpu is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_2eoqqzo3xdfvv22thjqnsrfsnm (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_2eoqqzo3xdfvv22thjqnsrfsnm: restore the original route: constraint failed: injected (1811)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_2eoqqzo3xdfvv22thjqnsrfsnm rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_2eoqqzo3xdfvv22thjqnsrfsnm is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_wb3yigi4omt6fk3wwu5gsihb7u (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_wb3yigi4omt6fk3wwu5gsihb7u: restore the original route: constraint failed: injected (1811)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_wb3yigi4omt6fk3wwu5gsihb7u rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_wb3yigi4omt6fk3wwu5gsihb7u is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_ahj6ov4x6tehkxqzlw37t46zm4 (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_ahj6ov4x6tehkxqzlw37t46zm4 is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_x5okutmkxq37qsadfawgp3vczy (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_x5okutmkxq37qsadfawgp3vczy rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_x5okutmkxq37qsadfawgp3vczy: delete its snapshot: injected failure at delete-snapshot
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_x5okutmkxq37qsadfawgp3vczy is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_qskyqmvan7rhtsayjhq6iwurm4 (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_qskyqmvan7rhtsayjhq6iwurm4 rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_qskyqmvan7rhtsayjhq6iwurm4 is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_yjmsuclz4th66akwtewuiqa4xe (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_yjmsuclz4th66akwtewuiqa4xe rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_yjmsuclz4th66akwtewuiqa4xe: delete the host's replica: injected failure at delete-replica
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_yjmsuclz4th66akwtewuiqa4xe is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_k67vsz6qikrjedruzowgpsls5m (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_k67vsz6qikrjedruzowgpsls5m rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_k67vsz6qikrjedruzowgpsls5m is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_ezt4qxhysgvrmlphhyhftpsouu (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_ezt4qxhysgvrmlphhyhftpsouu rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_ezt4qxhysgvrmlphhyhftpsouu: retire its journal row: constraint failed: injected (1811)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_ezt4qxhysgvrmlphhyhftpsouu is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_36p2qaqjeqzyzcx446toarloci (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_36p2qaqjeqzyzcx446toarloci rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_36p2qaqjeqzyzcx446toarloci: retire its journal row: constraint failed: injected (1811)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_36p2qaqjeqzyzcx446toarloci is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_tj6gbow4jfwgz222c2ka4yqccu (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_tj6gbow4jfwgz222c2ka4yqccu rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_tj6gbow4jfwgz222c2ka4yqccu is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: mark import sd_tj6gbow4jfwgz222c2ka4yqccu rolled back: injected failure at cas
2026/10/24 20:55:59 provision: a.shop.example.com: aborted seed sd_tj6gbow4jfwgz222c2ka4yqccu rolled back (no box had swapped it)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_3hplwrtesrxotkmbtatxrftf5a (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_3hplwrtesrxotkmbtatxrftf5a rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_3hplwrtesrxotkmbtatxrftf5a is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: aborted seed sd_3hplwrtesrxotkmbtatxrftf5a rolled back (no box had swapped it)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_xdmjy75pn4tfqwtqxxyh3nn4ce (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_xdmjy75pn4tfqwtqxxyh3nn4ce rollback deferred: the host is not fenced and stopped yet (<nil>)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_xdmjy75pn4tfqwtqxxyh3nn4ce rollback deferred: the host is not fenced and stopped yet (<nil>)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_xdmjy75pn4tfqwtqxxyh3nn4ce rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_xdmjy75pn4tfqwtqxxyh3nn4ce is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_p3yszwl5scnrecwlxltaq4m2si (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_p3yszwl5scnrecwlxltaq4m2si rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_p3yszwl5scnrecwlxltaq4m2si is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_v6hd6apasvdplsajoup5hjrcpq (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_v6hd6apasvdplsajoup5hjrcpq rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_v6hd6apasvdplsajoup5hjrcpq is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_ncklt65jjodz2qjpfsvxcdebmq (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_ncklt65jjodz2qjpfsvxcdebmq rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_ncklt65jjodz2qjpfsvxcdebmq is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_wyksp4s2ygv523j4e2hbacnoh4 (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_wyksp4s2ygv523j4e2hbacnoh4 rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_wyksp4s2ygv523j4e2hbacnoh4 is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_4nmxd554f6tf3iws3tulrjdv3q (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_4nmxd554f6tf3iws3tulrjdv3q rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_4nmxd554f6tf3iws3tulrjdv3q is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: minted instance route a.shop.example.com (app "other" acct "bac")
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_yuyolmhme632ovbtdj55jqkwei (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_yuyolmhme632ovbtdj55jqkwei rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_yuyolmhme632ovbtdj55jqkwei is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: minted instance route a.shop.example.com (app "other" acct "bac")
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_efd32g3evq4wtzhzdogf6u3zyi (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_efd32g3evq4wtzhzdogf6u3zyi rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_efd32g3evq4wtzhzdogf6u3zyi is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: minted instance route a.shop.example.com (app "other" acct "bac")
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_r3ispuknm3l6ofsscwzto25imy (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_r3ispuknm3l6ofsscwzto25imy has no route on this box: its local data is removed and no route is put back
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_r3ispuknm3l6ofsscwzto25imy is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_2czgur4ulisypdafdfyk7oml3i (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_wij6g4zjwwwbkxmn3lvltbfidy (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_w6vm7hslp6wypvzxovwwbdqqqa (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_w6vm7hslp6wypvzxovwwbdqqqa cannot roll back on this box: the activator is not wired; it stays journaled and gated
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_oj76wdpzxgohbognzhiugejuge (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_oj76wdpzxgohbognzhiugejuge is not rolled back: the route changed under the fence, so nothing there is touched
2026/10/24 20:55:59 provision: a.shop.example.com: import "../not-a-seed" names no snapshot and no replica; only its journal row is cleaned up
2026/10/24 20:55:59 provision: a.shop.example.com: import ../not-a-seed is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_xp5x6fnhce5pxd2zappvktrntu (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_pyppj3bi54za3tzqhn4q3xfmke (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_rl6pg2k72dcojzeffwiufowrg4 (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_lwfl6tkzbedw5zvubuqvrsua3u (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_v3jteye4saxfmjx7wj4vsanulm (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_v3jteye4saxfmjx7wj4vsanulm rollback deferred: the host is not fenced and stopped yet (stop evidence unreadable)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_j3bhvtwo6u7gh7ieauxxyhkb2u (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_j3bhvtwo6u7gh7ieauxxyhkb2u has no route on this box: its local data is removed and no route is put back
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_j3bhvtwo6u7gh7ieauxxyhkb2u is cleaned up and its journal row retired
2026/10/24 20:55:59 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_peqmnnqmydnrxjml44yis4vlbu (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_peqmnnqmydnrxjml44yis4vlbu: remove its local data: injected failure at remove-local
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_peqmnnqmydnrxjml44yis4vlbu is not rolled back: the route at the host is not the import's, so nothing there is touched
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_n2msnswvvtmbiven3z2dol3u3y (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_n2msnswvvtmbiven3z2dol3u3y: a route appeared at the host, so its local database is left and the rollback waits
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_etf5cgqmkwed7ugmc3lr6y4guu (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_etf5cgqmkwed7ugmc3lr6y4guu rollback deferred: no fence generation: the generation source answered 0
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_3us26rfav6rs4r2rsm3khjo2cy (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_3us26rfav6rs4r2rsm3khjo2cy rollback deferred: no fence generation: registry busy
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_dqfyrhmjfavfjeolz4tew6gnam (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_onjptmuvanrseoi3zszjxejns4 (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_wlfpszkf4cpwwistpbxzzdihem (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import deferred: this box cannot read the instance's env file
2026/10/24 20:55:59 provision: a.shop.example.com: import deferred: the instance's env file could not be read: a symlink
2026/10/24 20:55:59 provision: a.shop.example.com: swapped for import sd_adivfiiksfm6x5fjbilo22nsyq (the original route is journaled)
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_adivfiiksfm6x5fjbilo22nsyq rolled back to its original route
2026/10/24 20:55:59 provision: a.shop.example.com: import sd_adivfiiksfm6x5fjbilo22nsyq is cleaned up and its journal row retired
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_j7yh3tx64fxoqsg4epnsy22kxi (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_j7yh3tx64fxoqsg4epnsy22kxi rolled back to its original route
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_j7yh3tx64fxoqsg4epnsy22kxi is cleaned up and its journal row retired
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_5eoe5r7g4hrr3g3pz33cno2zyu (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_5eoe5r7g4hrr3g3pz33cno2zyu rolled back to its original route
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_5eoe5r7g4hrr3g3pz33cno2zyu is cleaned up and its journal row retired
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_emzjqdtmmaov4nde2ivyipwiee (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_emzjqdtmmaov4nde2ivyipwiee: retire its instance key: the env file is a symlink
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_emzjqdtmmaov4nde2ivyipwiee rolled back to its original route
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_emzjqdtmmaov4nde2ivyipwiee is cleaned up and its journal row retired
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_pibpceoescahcwrtxr7himxsm4 (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: row is on the nested layout (db "/data/instances/a.shop.example.com/a.shop.example.com.db" socket "/run/carlos/instances/a.shop.example.com/app.sock"); the record's shared paths are not drift
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_vzmnwcjoridtvcjbajux6yi2sm (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_rh5d4em5bw6f6vkmsw3h5k4hgi refused: there is no route at a.shop.example.com to replace
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_c3u6ndi4vfog2h4rhsbonczm6m refused: the route's address is 127.0.0.1:9, not 127.0.0.1:8401
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_zeerl6ete3dh6sft7vyykaur7q refused: the route's address is 127.0.0.1:8401,127.0.0.1:8402, not 127.0.0.1:8401
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_o4haap7papnqepm2um7tqbmkwy refused: the route's address is 127.0.0.1:9, not 127.0.0.1:8401
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_jjowg7nhzkf3rtgjzj4fdbe7ne refused: the route belongs to app other
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_clklr24ct7xkhf5gv6epc5ppbi refused: the route is filed under another account
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_opz3xdemqgecu4lax33eii3p4y refused: the route is filed under the operator account, and no assignment maps shop to this app
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_lf7i3tada2icrbgiaeabwulpa4 refused: the route is filed under the operator account, and no assignment maps shop to this app
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_spdyjos7k6vxlozfvxrjjffm5u refused: the route is filed under the operator account, and no assignment maps shop to this app
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_rzma7qxodurwo35ho5jdtchtay refused: the route is filed under the operator account, and no assignment maps shop to this app
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_xm64dn2dl5dlyfmd4kvlbm2lma refused: the route is not an address route this import can take over (socket)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_d7z3b2wsvzl4yvfm3ztrse4iva refused: the route is not an address route this import can take over (no address)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_leib6e6kr5l56y3bcjvsshkxfi refused: the route is not an address route this import can take over (provisioned)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_qfzgjqqsegdsdpwkobntyjgfam refused: the route is not an address route this import can take over (kind static)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_sbyewjxrslnde3y2tyout7th4y refused: the route is not an address route this import can take over (backing)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_s52qrzl4rj3eorgjqozd7eyw5y refused: the route is not an address route this import can take over (tunnel)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_xn5ayokhjq3ptundtpphbh7h2y refused: the route is not an address route this import can take over (caps)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_46nkrt4rh4kjbky2xgldltb6i4 refused: the route is not an address route this import can take over (platform)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_bxhrz2v62cdqqzuelrzp4paely refused: the route is not an address route this import can take over (canary)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_j7bud7krhcnqhj2u7bvn6zdgse refused: the route is not an address route this import can take over (kind sidecar)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_4yxjtnlyl3ykke77irgn2ovtlq refused: the route is not an address route this import can take over (region)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_7f65ouga2d2mjoosbrmy2ppq3m refused: the route is not an address route this import can take over (fleet_box)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_37bmu33pp65tftdzl7vvc5gfwe refused: the route is not an address route this import can take over (hibernate)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_kpybkyvuj6ddh4nxlq6b2qcswq refused: the route is not an address route this import can take over (claim_owner)
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_rh3uffeov4vln5cliq47sztsma refused: this box does not run instances nested
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_objrxf6jirl5u6gxgovokujqa4 refused: this box does not run instances nested
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_l64vgd4h6jv23he2qcgvundjrq refused: claim control is on for this box
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_jmt4zy3rkajuirpznsvkkxbd4a (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_ussrww25mncldsnlb4dwhlosoe (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: import deferred: this box cannot read the legacy route assignments
2026/10/24 20:56:00 provision: a.shop.example.com: import deferred: the legacy route assignments could not be read: store down
2026/10/24 20:56:00 provision: a.shop.example.com: not swapping for import sd_7cxcctl3hflevh2kehs3hmzpc4: its verify hash is not a sha256
2026/10/24 20:56:00 provision: a.shop.example.com: not swapping for import sd_zjpdq43y6zv7sz427wbnuw7k2y: its verify hash is not a sha256
2026/10/24 20:56:00 provision: a.shop.example.com: not swapping for import sd_ohctgdy5f6d2oej6v3yayusea4: its verify hash is not a sha256
2026/10/24 20:56:00 provision: a.shop.example.com: not swapping for import sd_../../x: its host or seed id is malformed (seeds: not a seed id)
2026/10/24 20:56:00 provision: A.shop.example.com/..: not swapping for import sd_6z7ndvy627tfaefwgpcwxk26ti: its host or seed id is malformed (seeds: not a host)
2026/10/24 20:56:00 provision: a.shop.example.com: not swapping for import sd_yoxx6l6cue7py5irzc23cztjjy: its record does not describe a nested, hibernating instance
2026/10/24 20:56:00 provision: a.shop.example.com: not swapping for import sd_34llnaojvn75uyvdd4yizoc3ce: its record does not describe a nested, hibernating instance
2026/10/24 20:56:00 provision: a.shop.example.com: not swapping for import sd_67em7cromjm65q46gdng5qxlsu: its record does not describe a nested, hibernating instance
2026/10/24 20:56:00 provision: a.shop.example.com: not swapping for import sd_4vksu7t2o3m5eb2rowrrwfd2ka: its record does not describe a nested, hibernating instance
2026/10/24 20:56:00 provision: a.shop.example.com: import deferred: the box's layout intent could not be read: store down
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_n2jyo5dnur7us5qrbnmkmtnpqm (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: import deferred: the box's nested probe did not answer: ready probe timed out
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_hcdaboxqokxq4v2iwrtkdnrd5m (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_qzprlsiezde7qs4tdrwqrpyksu (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: import deferred: the route is held by operation op_1
2026/10/24 20:56:00 provision: dry-run: would consider swapping a.shop.example.com for import sd_f6f6xfilspryr7llpvi76apwre
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_7tmeddnvs7y5njj6rk2qglnve4 rolled back to its original route
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_7tmeddnvs7y5njj6rk2qglnve4: the host's replica is left alone: the record apps/bac/shop/instances/a.shop.example.com.json holds the host
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_7tmeddnvs7y5njj6rk2qglnve4 is cleaned up and its journal row retired
2026/10/24 20:56:00 provision: a.shop.example.com: import sd_rtxfgabh3qpozxejo7xj7f7spq left alone: the import journal holds seed sd_uw64cijkw6leiucbmjhyi237sq at swapped
2026/10/24 20:56:00 provision: a.shop.example.com: not minting import sd_x7t2ghfemungdgm5lfdwf442ey: it is swapped but not committed, and only its swap makes its row
2026/10/24 20:56:00 provision: a.shop.example.com: not minting import sd_opq3sjyrljz57vbpl75aptma4q: it is placed but not committed, and only its swap makes its row
2026/10/24 20:56:00 provision: a.shop.example.com: swapped for import sd_3hbjgy3yp3weuxhg7usqf3zfy4 (the original route is journaled)
2026/10/24 20:56:00 provision: a.shop.example.com: not minting import sd_3hbjgy3yp3weuxhg7usqf3zfy4: it is swapped but not committed, and only its swap makes its row
2026/10/24 20:56:00 provision: a.shop.example.com: not minting import sd_3hbjgy3yp3weuxhg7usqf3zfy4: it is placed but not committed, and only its swap makes its row
2026/10/24 20:56:00 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/24 20:56:00 provision: removed retired sidecar route gone.eleven.bac.sidecar.internal (record gone)
2026/10/24 20:56:00 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:00 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:00 provision: converged sidecar bot.eleven.bac.sidecar.internal args ["sidecar","run"] -> ["worker","run"]
2026/10/24 20:56:00 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:00 provision: converged sidecar bot.eleven.bac.sidecar.internal ceilings 300/3600 -> 60/120
2026/10/24 20:56:00 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:00 provision: converged sidecar bot.eleven.bac.sidecar.internal channel stable -> canary/eleven
2026/10/24 20:56:00 provision: converged sidecar bot.eleven.bac.sidecar.internal environment "other" -> "relay"
2026/10/24 20:56:00 provision: converged sidecar bot.eleven.bac.sidecar.internal hibernate true -> false
2026/10/24 20:56:00 provision: skipping sidecar record apps/bac/eleven/sidecars/bot.json: read failed: failStore: reads disabled under apps/bac/eleven/sidecars/
2026/10/24 20:56:00 provision: skipping sidecar record apps/bac/eleven/sidecars/bot.json: malformed: invalid character 'n' looking for beginning of object key string
2026/10/24 20:56:00 provision: not removing sidecar bot.eleven.bac.sidecar.internal: its record exists but could not be read cleanly this pass
2026/10/24 20:56:00 provision: skipping sidecar record apps/bac/eleven/sidecars/bot.json: empty channel
2026/10/24 20:56:00 provision: skipping sidecar record apps/bac/eleven/sidecars/held.json: empty channel
2026/10/24 20:56:00 provision: not removing sidecar held.eleven.bac.sidecar.internal: its record exists but could not be read cleanly this pass
2026/10/24 20:56:00 provision: removed retired sidecar route old.eleven.bac.sidecar.internal (record gone)
2026/10/24 20:56:01 provision: bac/eleven has sidecar records but no sidecars manifest — not minting
2026/10/24 20:56:01 provision: bac/eleven is in the trash — not minting sidecar routes for it
2026/10/24 20:56:01 provision: sidecar bot.eleven.bac.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:56:01 provision: sidecar hub.relay.bac.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:56:01 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:01 provision: minted sidecar route hub.relay.bac.sidecar.internal (app "relay" acct "bac" args ["relay"])
2026/10/24 20:56:01 provision: sidecar bot.eleven.bac.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:56:01 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:01 provision: sidecar bot.eleven.bac.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:56:01 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:01 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:01 provision: sidecar bot.eleven.bac.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:56:01 provision: removed retired sidecar route bot.eleven.bac.sidecar.internal (record gone)
2026/10/24 20:56:01 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:01 provision: sidecar bot.eleven.bac.sidecar.internal is not placeable here: it asks for "no-inbound" placement and this box accepts inbound work (CARLOS_INBOUND="")
2026/10/24 20:56:01 provision: skipping sidecar record apps/bac/relay/sidecars/hub.json: read failed: failStore: reads disabled under apps/bac/relay/sidecars/
2026/10/24 20:56:01 provision: REFUSING to mint sidecar bot.eleven.bac.sidecar.internal: bot.eleven.bac.sidecar.internal-sidecar.env is staged on this box — the legacy file co-spawn and a native sidecar row are two mechanisms for one host; remove the file to migrate this host onto sidecar records
2026/10/24 20:56:01 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/24 20:56:01 provision: dry-run: would mint sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" channel "stable" args ["sidecar","run"])
2026/10/24 20:56:01 provision: dry-run: would remove retired sidecar route gone.eleven.bac.sidecar.internal (record gone)
2026/10/24 20:56:01 provision: bot.eleven.bac.sidecar.internal exists but is not a sidecar row this pass owns (kind "sidecar" provisioned false app "eleven" acct "bac") — leaving it alone
2026/10/24 20:56:01 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:56:01 provision: converged acme.go.tito.io update settings "{\"updates\":\"manual\",\"channel\":\"stable\"}" -> ""
2026/10/24 20:56:01 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:56:01 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:56:01 provision: manifest bac/tito: read failed: provision: manifest bac/tito: unexpected end of JSON input
2026/10/24 20:56:01 provision: manifest bac/tito: read failed: provision: manifest bac/tito: unexpected end of JSON input
2026/10/24 20:56:01 provision: acme.go.tito.io: not minted this pass: the app's update policy could not be read
2026/10/24 20:56:01 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/24 20:56:01 provision: manifest bac/tito: read failed: provision: manifest bac/tito: unexpected end of JSON input
2026/10/24 20:56:01 provision: acme.go.tito.io: not minted this pass: the app's update policy could not be read
2026/10/24 20:56:01 provision: minted instance route acme.go.tito.io held landing by mv_acme (app "tito" acct "bac")
2026/10/24 20:56:01 provision: record view: change index unusable (release: change index unreadable: store has no conditional read: unsupported operation): the walk records no generations
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: listing apps/bqd/: listdirs refused — its records are unseen this pass
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: listing apps/bqd/: listdirs refused — its records are unseen this pass
2026/10/24 20:56:01 provision: listing apps/bqd/: listdirs refused — its records are unseen this pass
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: listing apps/bqd/: listdirs refused — its records are unseen this pass
2026/10/24 20:56:01 provision: record view: change index unusable (release: change index malformed: invalid character 'n' looking for beginning of object key string): walking
2026/10/24 20:56:01 provision: record view: change index unusable (get refused): walking
2026/10/24 20:56:01 provision: record view: re-reading apps/bqc/shop failed: retrying on the next refresh
2026/10/24 20:56:01 provision: listing apps/bqc/shop/instances/: context canceled — its records are unseen this pass
2026/10/24 20:56:01 provision: record view: re-reading apps/bqc/shop failed: retrying on the next refresh
2026/10/24 20:56:01 provision: listing apps/bqc/shop/instances/: list refused — its records are unseen this pass
2026/10/24 20:56:01 provision: listing apps/bqc/shop/instances/: list refused — its records are unseen this pass
2026/10/24 20:56:01 provision: an app's records could not be listed: they are skipped
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: listing apps/bqd/: listdirs refused — its records are unseen this pass
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping accounts/bqd/account.json: malformed account manifest: invalid character 'n' looking for beginning of object key string
2026/10/24 20:56:01 provision: skipping accounts/bqd/account.json: malformed account manifest: invalid character 'n' looking for beginning of object key string
2026/10/24 20:56:01 provision: skipping accounts/bqd/account.json: malformed account manifest: invalid character 'n' looking for beginning of object key string
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: record view: re-reading accounts/bqd failed: retrying on the next refresh
2026/10/24 20:56:01 provision: skipping accounts/bqd/account.json: read failed: list refused
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: minted instance route one.example.com (app "shop" acct "bum")
2026/10/24 20:56:01 provision: removed retired instance route one.example.com (record gone)
2026/10/24 20:56:01 provision: minted instance route one.example.com (app "shop" acct "bum")
2026/10/24 20:56:01 provision: minted instance route one.example.com (app "shop" acct "bum")
2026/10/24 20:56:01 provision: removed retired instance route one.example.com (record gone)
2026/10/24 20:56:01 provision: skipping record apps/bum/shop/instances/one.example.com.json: empty channel
2026/10/24 20:56:01 provision: not removing one.example.com: its record exists but could not be read cleanly this pass
2026/10/24 20:56:01 provision: skipping record apps/bum/shop/instances/one.example.com.json: empty channel
2026/10/24 20:56:01 provision: not removing one.example.com: its record exists but could not be read cleanly this pass
2026/10/24 20:56:01 provision: record view: change index unusable (context deadline exceeded): walking
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/24 20:56:01 provision: record view: change index unusable (context deadline exceeded): the walk records no generations
2026/10/24 20:56:01 provision: skipping record apps/bqe/store/instances/bad.store.example.com.json: malformed: provision: invalid maintenance projection or protected fields
--- FAIL: TestPlan14RootReadCensus (14.55s)
    plan14_root_reads_test.go:600: root read census: 15 discrepancies
        changed root read internal/instancectl/server.go:parseRequest; review class, task, sites and digest
        changed root read internal/instancectl/server.go:type Server; review class, task, sites and digest
        changed root read internal/ops/hibernate_verify.go:HibernatingHosts; review class, task, sites and digest
        changed root read internal/ops/litestream.go:FleetDBsWithSkipped; review class, task, sites and digest
        changed root read internal/ops/ops.go:runLitestream; review class, task, sites and digest
        changed root source envelope internal/instancectl
        changed root source envelope internal/instanceexec
        changed root source envelope internal/ops
        unclassified root read internal/ops/litestream.go:ActivatorReplicated registry:Backing@59,registry:DBPath@97,registry:Hibernate@117
        unclassified root read internal/ops/litestream.go:ActivatorReplicatedPaths registry:DBPath@257,whole:Route@178,whole:Route@212
        changed legacy declaration internal/instancectl/server.go:parseRequest
        changed legacy declaration internal/instancectl/server.go:type Server
        changed legacy declaration internal/ops/hibernate_verify.go:HibernatingHosts
        changed legacy declaration internal/ops/litestream.go:FleetDBsWithSkipped
        changed legacy declaration internal/ops/ops.go:runLitestream
        
        make census-card shows what changed; make census-propose NAME=<branch> CLAIMS=<file> writes the overlay and its approval card (AGENTS.md, The census)
FAIL
FAIL	github.com/carlosframework/platform/internal/provision	23.193s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.010s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	5.551s
ok  	github.com/carlosframework/platform/internal/relay	7.231s
ok  	github.com/carlosframework/platform/internal/release	25.679s
ok  	github.com/carlosframework/platform/internal/rollout	3.782s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.004s
ok  	github.com/carlosframework/platform/internal/rootaws	0.009s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.060s
ok  	github.com/carlosframework/platform/internal/rootstate	0.027s
ok  	github.com/carlosframework/platform/internal/runs	0.619s
ok  	github.com/carlosframework/platform/internal/s3compat	0.005s
ok  	github.com/carlosframework/platform/internal/safefile	0.004s
ok  	github.com/carlosframework/platform/internal/schedule	2.599s
ok  	github.com/carlosframework/platform/internal/seeds	0.015s
ok  	github.com/carlosframework/platform/internal/sim/engines	2.560s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	10.231s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	4.647s
ok  	github.com/carlosframework/platform/internal/status	0.077s
ok  	github.com/carlosframework/platform/internal/steering	0.006s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.281s
ok  	github.com/carlosframework/platform/internal/svcrole	0.002s
ok  	github.com/carlosframework/platform/internal/telemetry	0.005s
ok  	github.com/carlosframework/platform/internal/termfence	0.004s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.004s
ok  	github.com/carlosframework/platform/internal/unixdial	0.002s
ok  	github.com/carlosframework/platform/internal/updatewords	0.006s
ok  	github.com/carlosframework/platform/internal/usage	0.008s
ok  	github.com/carlosframework/platform/internal/version	0.002s
ok  	github.com/carlosframework/platform/internal/vet	1.695s
FAIL
=== internal_console@0 (82 s)
ok  	github.com/carlosframework/platform/internal/console	80.501s
=== internal_console@1 (42 s)
ok  	github.com/carlosframework/platform/internal/console	41.297s
=== internal_console@2 (41 s)
ok  	github.com/carlosframework/platform/internal/console	39.756s
=== internal_console@3 (42 s)
ok  	github.com/carlosframework/platform/internal/console	40.487s
=== internal_console@4 (42 s)
ok  	github.com/carlosframework/platform/internal/console	40.666s
=== internal_console@5 (41 s)
ok  	github.com/carlosframework/platform/internal/console	39.519s
=== internal_console@6 (35 s)
ok  	github.com/carlosframework/platform/internal/console	34.364s
=== internal_console@7 (46 s)
ok  	github.com/carlosframework/platform/internal/console	44.797s
=== root@0 (109 s)
ok  	github.com/carlosframework/platform	107.770s
=== root@1 (83 s)
ok  	github.com/carlosframework/platform	82.103s
=== root@2 (92 s)
using http://127.0.0.1:32907 as a@keymail.dev
using http://127.0.0.1:36779 as vicky@keymail.dev
titogo (staging):
KEY   VALUE
PORT  8080
--- FAIL: TestRunBuiltCopyIsApplied (0.12s)
    approved_run_copy_test.go:139: RUN-C13: internal/instanceexec/exec.go does not contain the approved text "instance-exec: role <role> is not run, seed, sidecar or cmd"
    approved_run_copy_test.go:139: RUN-C14: internal/instanceexec/exit.go does not contain the approved text "instance-exit: role <role> is not run, seed, sidecar or cmd"
2026/10/24 20:56:05 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:56:05 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A41899": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:05 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:56:05 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A41899": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:05 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:56:05 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:56:05 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A45057": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:05 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:56:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:56:07 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A40233": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:07 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:56:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:56:07 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A45545": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:07 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:56:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:56:09 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A35399": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:09 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:56:11 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:56:11 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A43625": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:11 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
using http://127.0.0.1:45213 as paul@keymail.dev
account: ops (bab)
2026/10/24 20:56:14 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A45213": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:14 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
set 1 env var(s) for titogo (default)
using http://127.0.0.1:45213 as paul@keymail.dev
account: ops (bab)
set 1 secret(s) for titogo (default)
account: ops (bab)
account: ops (bab)
using http://127.0.0.1:45213 as paul@keymail.dev
account: ops (bab)
config sync requested for titogo the files land within seconds; programs carlos owns restart themselves, and anything else keeps its old values until you restart it
2026/10/24 20:56:14 console: delivery to mallory@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=mallory%40keymail.dev&from=console%40127.0.0.1%3A45213": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:14 console: could not notify mallory@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:56:16 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 20:56:16 INFO ltx file uploaded system=store db=shop.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=708
2026/10/24 20:56:16 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 20:56:17 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000001 txid.db=0000000000000002
2026/10/24 20:56:17 INFO ltx file uploaded system=store db=shop.db replica=file level=0 minTXID=0000000000000002 maxTXID=0000000000000002 size=265
2026/10/24 20:56:17 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000002 txid.db=0000000000000002
2026/10/24 20:56:17 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:56:17 console: delivery to member@example.com: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=member%40example.com&from=console%40127.0.0.1%3A33815": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:17 console: could not notify member@example.com of a CLI token issue: console: no email sender is configured
2026/10/24 20:56:17 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 20:56:17 INFO ltx file uploaded system=store db=shop.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=708
2026/10/24 20:56:17 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 20:56:18 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000001 txid.db=0000000000000002
2026/10/24 20:56:18 INFO ltx file uploaded system=store db=shop.db replica=file level=0 minTXID=0000000000000002 maxTXID=0000000000000002 size=265
2026/10/24 20:56:18 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000002 txid.db=0000000000000002
2026/10/24 20:56:19 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:56:19 console: delivery to member@example.com: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=member%40example.com&from=console%40127.0.0.1%3A46157": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:56:19 console: could not notify member@example.com of a CLI token issue: console: no email sender is configured
2026/10/24 20:57:00 console: db read dbr_gk6p3qoabk67zgrkcxhm6eczm4: relayed 1 chunks (101 bytes) in 41.03s, ended done
2026/10/24 20:57:00 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 20:57:00 INFO ltx file uploaded system=store db=shop.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=708
2026/10/24 20:57:00 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 20:57:01 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000001 txid.db=0000000000000002
2026/10/24 20:57:01 INFO ltx file uploaded system=store db=shop.db replica=file level=0 minTXID=0000000000000002 maxTXID=0000000000000002 size=265
2026/10/24 20:57:01 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000002 txid.db=0000000000000002
2026/10/24 20:57:01 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:57:01 console: delivery to member@example.com: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=member%40example.com&from=console%40127.0.0.1%3A38263": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:57:01 console: could not notify member@example.com of a CLI token issue: console: no email sender is configured
2026/10/24 20:57:02 console: db read dbr_6ljax5twyqpva3zw7zmhixtgxe: relayed 1 chunks (1070 bytes) in 1.001s, ended done
2026/10/24 20:57:03 console: db read dbr_6bsavm5qgph7yneffoksorzske: relayed 1 chunks (805 bytes) in 1.001s, ended done
2026/10/24 20:57:03 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 20:57:03 INFO ltx file uploaded system=store db=shop.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=708
2026/10/24 20:57:03 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 20:57:04 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000001 txid.db=0000000000000002
2026/10/24 20:57:04 INFO ltx file uploaded system=store db=shop.db replica=file level=0 minTXID=0000000000000002 maxTXID=0000000000000002 size=265
2026/10/24 20:57:04 INFO replica sync system=store db=shop.db replica=file txid.replica=0000000000000002 txid.db=0000000000000002
2026/10/24 20:57:04 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:57:04 console: delivery to member@example.com: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=member%40example.com&from=console%40127.0.0.1%3A43597": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:57:04 console: could not notify member@example.com of a CLI token issue: console: no email sender is configured
2026/10/24 20:57:05 console: db read dbr_aspfdnahcfpzkbeceel3pz2a7a: relayed 2 chunks (8386 bytes) in 1.044s, ended client gone
using http://127.0.0.1:38015 as paul@keymail.dev
account: ops (bab)
attached tickets.example.com -> default.go.tito.io
DNS: pointing
using http://127.0.0.1:38015 as paul@keymail.dev
account: ops (bab)
attached carloku.example.com -> default.go.tito.io
2026/10/24 20:57:06 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:57:06 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A35727": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:57:06 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:57:06 console: delivery to rival@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=rival%40keymail.dev&from=console%40127.0.0.1%3A35727": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:57:06 console: could not notify rival@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:57:15 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A46081": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:57:15 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:57:15 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 20:57:15 console: delivery to paul@keymail.dev: keymail lookup unreachable, trying email: keymail: lookup unreachable: Get "http://keymail.invalid/api/federation/lookup?addr=paul%40keymail.dev&from=console%40127.0.0.1%3A34375": dial tcp: lookup keymail.invalid on 127.0.0.53:53: no such host
2026/10/24 20:57:15 console: could not notify paul@keymail.dev of a CLI token issue: console: no email sender is configured
2026/10/24 20:57:16 edge: static oldsite/stable: resolve channel: dirstore: apps/bab/oldsite/channels/stable.json: file does not exist — if this deployment predates account-scoped bucket keys its objects are still at apps/oldsite/, which the runtime no longer reads; move them once with: carlos accounts migrate --app oldsite --from legacy --to bab
2026/10/24 20:57:16 domains: minted platform domain blog.mdbbndjqx.oncarlos.test (upstream blog.kass.test)
2026/10/24 20:57:16 domains: minted platform domain blog.gdpkwhwrw.oncarlos.test (upstream blog.woodstar.test)
2026/10/24 20:57:16 domains: minted platform domain oldsite.bab.oncarlos.test (upstream old.test)
2026/10/24 20:57:16 edge: static oldsite/stable: resolve channel: dirstore: apps/bab/oldsite/channels/stable.json: file does not exist — if this deployment predates account-scoped bucket keys its objects are still at apps/oldsite/, which the runtime no longer reads; move them once with: carlos accounts migrate --app oldsite --from legacy --to bab
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 20:57:16 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=720
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 20:57:16 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=720
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000002
2026/10/24 20:57:16 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000002 maxTXID=0000000000000002 size=370
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000002
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000002
2026/10/24 20:57:16 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000002 maxTXID=0000000000000002 size=370
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000002
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000003
2026/10/24 20:57:16 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000003 maxTXID=0000000000000003 size=454
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000003 txid.db=0000000000000003
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000003
2026/10/24 20:57:16 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000003 maxTXID=0000000000000003 size=454
2026/10/24 20:57:16 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000003 txid.db=0000000000000003
FAIL
FAIL	github.com/carlosframework/platform	90.774s
FAIL

FAILED: rest root@2
make: *** [Makefile:19: test] Error 1

amadan: step gate failed: exit status 2

amadan: job failed at step gate — its output is above, not at the tail of this log
diff --git a/implementation/census-claims-sidecar-isolation.txt b/implementation/census-claims-sidecar-isolation.txt
index babd959e4..73431fbaf 100644
--- a/implementation/census-claims-sidecar-isolation.txt
+++ b/implementation/census-claims-sidecar-isolation.txt
@@ -1,3 +1,5 @@
internal/ops/ops.go:runLitestream shrink guard accepts and names a drop because the row is ActivatorReplicated (CoverageShortfallExcept); other drops still refuse
internal/ops/hibernate_verify.go:HibernatingHosts selects by ActivatorReplicated, so an always-on worker is drilled; reads the whole route through the predicate
internal/ops/litestream.go:FleetDBsWithSkipped the activator-owned exclusion tests ActivatorReplicated (workers included) instead of Hibernate alone; reads the whole route through the predicate
+internal/instancectl/server.go:parseRequest admits the worker role on /start, /stop and /reset and lets a worker start name its staged exe (validated by host, D1/D5); a worker with a generation is still refused; the role error names worker; no new read
+internal/instancectl/server.go:type Server the #292 start fence maps (legacyStarts, legacyStopping, legacyBarrier) are keyed by roleHost{role, host} instead of host, so the worker unit is fenced apart from the run unit (D19); no new read
diff --git a/internal/instancectl/legacystart.go b/internal/instancectl/legacystart.go
index 35355f2b6..d6c823039 100644
--- a/internal/instancectl/legacystart.go
+++ b/internal/instancectl/legacystart.go
@@ -8,29 +8,34 @@ import (
"time"
)
-// Legacy run start attempts (#292, Astra round 2 P1, the owner's ruling).
+// Legacy start attempts (#292, Astra round 2 P1, the owner's ruling;
+// widened to the worker role by sidecar isolation D19, #370).
//
// The agent proves an aborted start's unit stopped through /stop before it
// gives the lease back. Cancelling its /start does not join this side's
// handler, which could still submit the systemd start after that /stop had
// answered, if it had not yet seen the disconnect; systemd's replacement of
// a queued start by a stop only covers jobs that already exist. So, for the
-// original run unit (carlos-instance@, generation 0) only, start and stop
-// are ordered per host here:
+// original run unit (carlos-instance@, generation 0) and the worker unit
+// (carlos-instance-worker@, which never has a generation), start and stop
+// are ordered here, keyed by (role, host):
//
// - every /start registers an attempt with a context /stop can cancel,
// and checks that context immediately before it submits the start;
-// - a /stop fences the host: new starts are refused while it runs, every
-// attempt in flight is cancelled and joined (bounded), and only then is
-// the unit stopped, which replaces any job a joined attempt submitted;
+// - a /stop fences its (role, host): new starts of that unit are refused
+// while it runs, every attempt in flight is cancelled and joined
+// (bounded), and only then is the unit stopped, which replaces any job
+// a joined attempt submitted;
// - a start whose request was accepted at or before the moment a stop
-// fences the host (the accepted-connection watermark, connSeq) is an
+// fences the unit (the accepted-connection watermark, connSeq) is an
// earlier request, and is refused, however late its handler registers;
// one accepted after it runs normally. Serve answers one request per
// connection, so a connection's accept order is its request's.
//
-// Once /stop has answered, no start from an earlier request is in flight or
-// can be submitted afterwards. Generation units, seeds, sidecars and the
+// Once /stop has answered, no start of that unit from an earlier request is
+// in flight or can be submitted afterwards. The key is the role and the
+// host together (D19): a worker's stop never fences the same host's run
+// unit, nor the reverse. Generation units, seeds, legacy sidecars and the
// claim verbs are untouched.
// legacyJoinBudget bounds how long a /stop waits for the start attempts it
@@ -38,7 +43,18 @@ import (
// the stop answers an error and the agent keeps the lease.
const legacyJoinBudget = 10 * time.Second
-// legacyAttempt is one in-flight legacy run start.
+// roleHost keys the fence: one unit, the role's template instance for the
+// host. Only RoleRun and RoleWorker are ever fenced (fencedRole).
+type roleHost struct {
+ role Role
+ host string
+}
+
+// fencedRole reports whether a role's start and stop are ordered here: the
+// original run unit (#292) and the worker unit (D19).
+func fencedRole(role Role) bool { return role == RoleRun || role == RoleWorker }
+
+// legacyAttempt is one in-flight legacy start.
type legacyAttempt struct {
cancel context.CancelFunc
done chan struct{}
@@ -64,33 +80,34 @@ func requestSeq(ctx context.Context) uint64 {
return n
}
-// errLegacyFenced refuses a start while a stop of its host runs, or one
-// whose request came before a stop of its host.
-var errLegacyFenced = errors.New("a stop of this host's run unit has run since this start was asked for")
+// errLegacyFenced refuses a start while a stop of its unit runs, or one
+// whose request came before a stop of its unit.
+var errLegacyFenced = errors.New("a stop of this unit has run since this start was asked for")
-// beginLegacyStart registers a legacy run start of host for a request of
-// accept order seq. Its context is parent's, cancellable by a /stop of the
-// host; finish must be called when the handler returns.
-func (s *Server) beginLegacyStart(parent context.Context, host string, seq uint64) (context.Context, func(), error) {
+// beginLegacyStart registers a start of role's unit for host, for a request
+// of accept order seq. Its context is parent's, cancellable by a /stop of
+// the same role and host; finish must be called when the handler returns.
+func (s *Server) beginLegacyStart(parent context.Context, role Role, host string, seq uint64) (context.Context, func(), error) {
+ key := roleHost{role, host}
s.legacyMu.Lock()
defer s.legacyMu.Unlock()
- if s.legacyStopping[host] > 0 || (seq != 0 && seq <= s.legacyBarrier[host]) {
- return nil, nil, errLegacyFenced
+ if s.legacyStopping[key] > 0 || (seq != 0 && seq <= s.legacyBarrier[key]) {
+ return nil, nil, fmt.Errorf("%s unit of %s: %w", role, host, errLegacyFenced)
}
ctx, cancel := context.WithCancel(parent)
a := &legacyAttempt{cancel: cancel, done: make(chan struct{})}
if s.legacyStarts == nil {
- s.legacyStarts = make(map[string]map[*legacyAttempt]struct{})
+ s.legacyStarts = make(map[roleHost]map[*legacyAttempt]struct{})
}
- if s.legacyStarts[host] == nil {
- s.legacyStarts[host] = make(map[*legacyAttempt]struct{})
+ if s.legacyStarts[key] == nil {
+ s.legacyStarts[key] = make(map[*legacyAttempt]struct{})
}
- s.legacyStarts[host][a] = struct{}{}
+ s.legacyStarts[key][a] = struct{}{}
finish := func() {
s.legacyMu.Lock()
- delete(s.legacyStarts[host], a)
- if len(s.legacyStarts[host]) == 0 {
- delete(s.legacyStarts, host)
+ delete(s.legacyStarts[key], a)
+ if len(s.legacyStarts[key]) == 0 {
+ delete(s.legacyStarts, key)
}
s.legacyMu.Unlock()
cancel()
@@ -99,37 +116,38 @@ func (s *Server) beginLegacyStart(parent context.Context, host string, seq uint6
return ctx, finish, nil
}
-// fenceLegacyStarts is a /stop's first step for host: refuse new starts
-// while the stop runs, refuse every start accepted at or before this moment
-// (the accepted-connection watermark, read under legacyMu, so no start can
-// register between the watermark and the snapshot), cancel the attempts in
-// flight and join them, bounded by the join budget and ctx. The fence is
-// per host and in memory only: release lifts the refusal of new starts when
-// the stop is done, and only connections accepted before the watermark
-// stay refused. An error means an attempt did not return in time and may
-// still submit.
-func (s *Server) fenceLegacyStarts(ctx context.Context, host string) (func(), error) {
+// fenceLegacyStarts is a /stop's first step for role's unit of host: refuse
+// new starts of that unit while the stop runs, refuse every start of it
+// accepted at or before this moment (the accepted-connection watermark,
+// read under legacyMu, so no start can register between the watermark and
+// the snapshot), cancel the attempts in flight and join them, bounded by
+// the join budget and ctx. The fence is per (role, host) and in memory
+// only: release lifts the refusal of new starts when the stop is done, and
+// only connections accepted before the watermark stay refused. An error
+// means an attempt did not return in time and may still submit.
+func (s *Server) fenceLegacyStarts(ctx context.Context, role Role, host string) (func(), error) {
+ key := roleHost{role, host}
s.legacyMu.Lock()
seq := s.connSeq.Load()
if s.legacyStopping == nil {
- s.legacyStopping = make(map[string]int)
+ s.legacyStopping = make(map[roleHost]int)
}
if s.legacyBarrier == nil {
- s.legacyBarrier = make(map[string]uint64)
+ s.legacyBarrier = make(map[roleHost]uint64)
}
- s.legacyStopping[host]++
- if seq > s.legacyBarrier[host] {
- s.legacyBarrier[host] = seq
+ s.legacyStopping[key]++
+ if seq > s.legacyBarrier[key] {
+ s.legacyBarrier[key] = seq
}
var inFlight []*legacyAttempt
- for a := range s.legacyStarts[host] {
+ for a := range s.legacyStarts[key] {
inFlight = append(inFlight, a)
}
s.legacyMu.Unlock()
release := func() {
s.legacyMu.Lock()
- if s.legacyStopping[host]--; s.legacyStopping[host] <= 0 {
- delete(s.legacyStopping, host)
+ if s.legacyStopping[key]--; s.legacyStopping[key] <= 0 {
+ delete(s.legacyStopping, key)
}
s.legacyMu.Unlock()
}
@@ -144,9 +162,9 @@ func (s *Server) fenceLegacyStarts(ctx context.Context, host string) (func(), er
select {
case <-a.done:
case <-timer.C:
- return release, fmt.Errorf("a start of %s is still in flight after %s", host, budget)
+ return release, fmt.Errorf("a %s start of %s is still in flight after %s", role, host, budget)
case <-ctx.Done():
- return release, fmt.Errorf("a start of %s is still in flight: %w", host, ctx.Err())
+ return release, fmt.Errorf("a %s start of %s is still in flight: %w", role, host, ctx.Err())
}
}
return release, nil
diff --git a/internal/instancectl/legacystart_live_test.go b/internal/instancectl/legacystart_live_test.go
index 617e9a0ff..08da47684 100644
--- a/internal/instancectl/legacystart_live_test.go
+++ b/internal/instancectl/legacystart_live_test.go
@@ -20,12 +20,12 @@ func TestADelayedStartAcceptedBeforeTheFenceIsRefusedWhenItRegisters(t *testing.
s, _ := newServer(t, &fakeCtl{})
_ = s.nextConnSeq() // connection 1: will carry the /stop
startConn := s.nextConnSeq() // connection 2: the delayed /start
- release, err := s.fenceLegacyStarts(context.Background(), host)
+ release, err := s.fenceLegacyStarts(context.Background(), RoleRun, host)
if err != nil {
t.Fatalf("fence: %v", err)
}
release()
- if _, finish, err := s.beginLegacyStart(context.Background(), host, startConn); err == nil {
+ if _, finish, err := s.beginLegacyStart(context.Background(), RoleRun, host, startConn); err == nil {
finish()
t.Fatal("a start accepted before the fence registered after it and was admitted")
}
@@ -77,7 +77,7 @@ func TestTheFenceCoversOnlyStartsAcceptedBeforeTheStop(t *testing.T) {
t.Fatalf("a start accepted after the stop returned: status %d, want 200", start.StatusCode)
}
s.legacyMu.Lock()
- stopping := s.legacyStopping[host]
+ stopping := s.legacyStopping[roleHost{RoleRun, host}]
s.legacyMu.Unlock()
if stopping != 0 {
t.Fatalf("the stop's refusal of new starts outlived it (%d)", stopping)
@@ -91,10 +91,16 @@ func TestTheFenceCoversOnlyStartsAcceptedBeforeTheStop(t *testing.T) {
}
}
-// heldStart parks host's legacy /start before its tier step until release
-// is closed, and returns once the handler is parked there. The handler does
-// not see its own cancellation (unseenCancel).
+// heldStart parks host's legacy run /start before its tier step until
+// release is closed, and returns once the handler is parked there. The
+// handler does not see its own cancellation (unseenCancel).
func heldStart(t *testing.T, ctl *fakeCtl, c *http.Client, host string) (release chan struct{}) {
+ t.Helper()
+ return heldStartRole(t, ctl, c, RoleRun, host)
+}
+
+// heldStartRole is heldStart for a fenced role (run or worker, D19).
+func heldStartRole(t *testing.T, ctl *fakeCtl, c *http.Client, role Role, host string) (release chan struct{}) {
t.Helper()
entered, gate := make(chan struct{}), make(chan struct{})
ctl.mu.Lock()
@@ -107,7 +113,7 @@ func heldStart(t *testing.T, ctl *fakeCtl, c *http.Client, host string) (release
}
ctl.unseenCancel = true
ctl.mu.Unlock()
- postAsync(c, "/start?host="+host+"&role=run&memory_max=536870912")
+ postAsync(c, "/start?host="+host+"&role="+string(role)+"&memory_max=536870912")
select {
case <-entered:
case <-time.After(5 * time.Second):
@@ -147,13 +153,25 @@ func TestAStuckJoinDelaysNoOtherHost(t *testing.T) {
// /stop of the host does not hang behind the stuck attempt: it answers the
// same error within the bound, and joins it once it returns.
func TestAStuckStopAnswersAndTheNextStopDoesNotHang(t *testing.T) {
- host := "wedged.example.com"
+ stuckStopAnswersWithinTheBound(t, RoleRun, "wedged.example.com")
+}
+
+// D19: a worker /stop whose held attempt does not return within the join
+// budget answers an error (502 naming the start in flight) rather than
+// 204, so the agent keeps the lease; the next stop does not hang either,
+// and once the attempt returns a stop answers 204.
+func TestWorkerJoinPastBudgetAnswersError(t *testing.T) {
+ stuckStopAnswersWithinTheBound(t, RoleWorker, "wedged-worker.example.com")
+}
+
+func stuckStopAnswersWithinTheBound(t *testing.T, role Role, host string) {
+ t.Helper()
ctl := &fakeCtl{answers: map[string]string{"show -p InvocationID --value": "inv1\n"}}
_, c := newServer(t, ctl, func(s *Server) { s.legacyJoin = 200 * time.Millisecond })
- gate := heldStart(t, ctl, c, host)
+ gate := heldStartRole(t, ctl, c, role, host)
stopOnce := func() (int, string, time.Duration) {
begun := time.Now()
- resp := post(t, c, "/stop?host="+host+"&role=run")
+ resp := post(t, c, "/stop?host="+host+"&role="+string(role))
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
return resp.StatusCode, string(body), time.Since(begun)
diff --git a/internal/instancectl/legacystart_test.go b/internal/instancectl/legacystart_test.go
index ad8b3acf9..fd3f7ae05 100644
--- a/internal/instancectl/legacystart_test.go
+++ b/internal/instancectl/legacystart_test.go
@@ -17,8 +17,20 @@ import (
// does not see that cancellation (unseenCancel): no start may be submitted
// once the /stop has returned.
func TestALegacyStartDelayedPastItsCancellationNeverSubmitsAfterAStop(t *testing.T) {
- host := "late.example.com"
- unit := UnitName(RoleRun, host)
+ heldStartNeverSubmitsAfterAStop(t, RoleRun, "late.example.com")
+}
+
+// Sidecar isolation D19 (#370): the worker unit gets the run unit's
+// ordering. A worker /start whose client gave up while the handler was
+// held is cancelled and joined by the worker /stop that follows, and the
+// held handler never submits once that stop has answered.
+func TestWorkerStopCancelsAndJoinsHeldStart(t *testing.T) {
+ heldStartNeverSubmitsAfterAStop(t, RoleWorker, "late-worker.example.com")
+}
+
+func heldStartNeverSubmitsAfterAStop(t *testing.T, role Role, host string) {
+ t.Helper()
+ unit := UnitName(role, host)
entered, gate := make(chan struct{}), make(chan struct{})
ctl := &fakeCtl{
answers: map[string]string{"show -p InvocationID --value": "inv1\n"},
@@ -34,7 +46,7 @@ func TestALegacyStartDelayedPastItsCancellationNeverSubmitsAfterAStop(t *testing
startDone := make(chan struct{})
go func() {
defer close(startDone)
- req, _ := http.NewRequestWithContext(startCtx, http.MethodPost, "http://unix/start?host="+host+"&role=run&memory_max=536870912", nil)
+ req, _ := http.NewRequestWithContext(startCtx, http.MethodPost, "http://unix/start?host="+host+"&role="+string(role)+"&memory_max=536870912", nil)
if resp, err := c.Do(req); err == nil {
resp.Body.Close()
}
@@ -49,7 +61,7 @@ func TestALegacyStartDelayedPastItsCancellationNeverSubmitsAfterAStop(t *testing
stopped := make(chan int, 1)
go func() {
- resp := post(t, c, "/stop?host="+host+"&role=run")
+ resp := post(t, c, "/stop?host="+host+"&role="+string(role))
resp.Body.Close()
stopped <- resp.StatusCode
}()
@@ -92,23 +104,92 @@ func TestALegacyStartDelayedPastItsCancellationNeverSubmitsAfterAStop(t *testing
// earlier request: refused once that stop has run, even if its handler only
// gets going afterwards.
func TestALegacyStartFromBeforeAStopIsRefused(t *testing.T) {
- host := "early.example.com"
+ startFromBeforeAStopIsRefused(t, RoleRun, "early.example.com")
+}
+
+// D19: the same watermark rule for the worker role.
+func TestWorkerStartAcceptedBeforeStopIsRefused(t *testing.T) {
+ startFromBeforeAStopIsRefused(t, RoleWorker, "early-worker.example.com")
+}
+
+func startFromBeforeAStopIsRefused(t *testing.T, role Role, host string) {
+ t.Helper()
ctl := &fakeCtl{answers: map[string]string{"show -p InvocationID --value": "inv1\n"}}
s, _ := newServer(t, ctl)
// Stamp two connections in accept order: the start's first.
startSeq := s.nextConnSeq()
_ = s.nextConnSeq() // the stop's own connection
- release, err := s.fenceLegacyStarts(context.Background(), host)
+ release, err := s.fenceLegacyStarts(context.Background(), role, host)
if err != nil {
t.Fatalf("fence: %v", err)
}
release()
- if _, _, err := s.beginLegacyStart(context.Background(), host, startSeq); err == nil {
+ if _, _, err := s.beginLegacyStart(context.Background(), role, host, startSeq); err == nil {
t.Fatal("a start accepted before the stop was admitted after it")
}
- if _, finish, err := s.beginLegacyStart(context.Background(), host, s.nextConnSeq()); err != nil {
+ if _, finish, err := s.beginLegacyStart(context.Background(), role, host, s.nextConnSeq()); err != nil {
t.Fatalf("a start accepted after the stop was refused: %v", err)
} else {
finish()
}
}
+
+// D19: the fence is keyed by host AND role. A worker's stop never fences
+// the same host's run unit: a run start accepted before the worker stop is
+// still admitted afterwards, and a run start or stop answers at once while
+// the worker stop is joining a stuck worker attempt.
+func TestWorkerStopDoesNotFenceRunUnit(t *testing.T) {
+ stopOfOneRoleDoesNotFenceTheOther(t, RoleWorker, RoleRun, "worker-then-run.example.com")
+}
+
+// The reverse: a run unit's stop never fences the same host's worker.
+func TestRunStopDoesNotFenceWorkerUnit(t *testing.T) {
+ stopOfOneRoleDoesNotFenceTheOther(t, RoleRun, RoleWorker, "run-then-worker.example.com")
+}
+
+func stopOfOneRoleDoesNotFenceTheOther(t *testing.T, stopping, other Role, host string) {
+ t.Helper()
+ ctl := &fakeCtl{answers: map[string]string{"show -p InvocationID --value": "inv1\n"}}
+ s, c := newServer(t, ctl)
+
+ // The watermark: a start of the other role accepted before this role's
+ // stop is not an earlier request of THIS fence.
+ otherSeq := s.nextConnSeq()
+ _ = s.nextConnSeq()
+ release, err := s.fenceLegacyStarts(context.Background(), stopping, host)
+ if err != nil {
+ t.Fatalf("fence: %v", err)
+ }
+ release()
+ if _, finish, err := s.beginLegacyStart(context.Background(), other, host, otherSeq); err != nil {
+ t.Fatalf("a %s start accepted before a %s stop was refused: %v", other, stopping, err)
+ } else {
+ finish()
+ }
+ if _, _, err := s.beginLegacyStart(context.Background(), stopping, host, otherSeq); err == nil {
+ t.Fatalf("a %s start accepted before a %s stop was admitted", stopping, stopping)
+ }
+
+ // The join: while this role's stop joins a held attempt, the other
+ // role's verbs on the same host pass through.
+ gate := heldStartRole(t, ctl, c, stopping, host)
+ stop := postAsync(c, "/stop?host="+host+"&role="+string(stopping))
+ defer func() { close(gate); <-stop }()
+ time.Sleep(50 * time.Millisecond) // the stop is joining
+ for _, path := range []string{"/start?host=" + host + "&role=" + string(other) + "&memory_max=536870912", "/stop?host=" + host + "&role=" + string(other)} {
+ done := postAsync(c, path)
+ select {
+ case code := <-done:
+ if code/100 != 2 {
+ t.Fatalf("%s during a %s stop: status %d", path, stopping, code)
+ }
+ case <-time.After(2 * time.Second):
+ t.Fatalf("%s waited behind a %s stop of the same host", path, stopping)
+ }
+ }
+ select {
+ case <-stop:
+ t.Fatalf("the %s stop answered while its attempt was still held", stopping)
+ default:
+ }
+}
diff --git a/internal/instancectl/names.go b/internal/instancectl/names.go
index 2dd8a4f1c..e1f3f3e80 100644
--- a/internal/instancectl/names.go
+++ b/internal/instancectl/names.go
@@ -22,7 +22,7 @@ import (
"github.com/carlosframework/platform/internal/provision"
)
-// Role is which of the four template units a request or a file is about.
+// Role is which of the five template units a request or a file is about.
type Role string
const (
@@ -32,12 +32,18 @@ const (
// RoleCmd is one `carlos run` command (run spec §5.4). "run" is the
// serving role already, so the command's role is cmd.
RoleCmd Role = "cmd"
+ // RoleWorker is a native sidecar's own unit (sidecar isolation D1,
+ // #370): a worker IS its host, so its unit and slice are keyed by the
+ // worker's host, not an instance's. The sidecar role stays the legacy
+ // co-spawn's, keyed by the instance it accompanies. A worker has one
+ // unit per host and never a generation.
+ RoleWorker Role = "worker"
)
-// ParseRole accepts exactly the four literals.
+// ParseRole accepts exactly the five literals.
func ParseRole(s string) (Role, bool) {
switch Role(s) {
- case RoleRun, RoleSeed, RoleSidecar, RoleCmd:
+ case RoleRun, RoleSeed, RoleSidecar, RoleCmd, RoleWorker:
return Role(s), true
}
return "", false
@@ -68,13 +74,14 @@ func Escape(s string) string {
return b.String()
}
-// The four template prefixes. A unit under any of them is the platform's
+// The five template prefixes. A unit under any of them is the platform's
// to start, stop and sweep; nothing else is (design §5.3).
const (
UnitPrefixRun = "carlos-instance@"
UnitPrefixSeed = "carlos-instance-seed@"
UnitPrefixSidecar = "carlos-instance-sidecar@"
UnitPrefixCmd = "carlos-instance-cmd@"
+ UnitPrefixWorker = "carlos-instance-worker@"
)
// CmdExeApp and CmdExeLive are the cmd template's executable mount
@@ -94,12 +101,14 @@ func prefixFor(role Role) string {
return UnitPrefixSidecar
case RoleCmd:
return UnitPrefixCmd
+ case RoleWorker:
+ return UnitPrefixWorker
}
return UnitPrefixRun
}
// UnitName is the template instance for a role and a host:
-// carlos-instance[-seed|-sidecar|-cmd]@<Escape(InstanceID(host))>.service.
+// carlos-instance[-seed|-sidecar|-cmd|-worker]@<Escape(InstanceID(host))>.service.
func UnitName(role Role, host string) string {
return prefixFor(role) + Escape(provision.InstanceID(host)) + ".service"
}
@@ -217,7 +226,7 @@ func Unescape(s string) (string, bool) {
return b.String(), true
}
-// ParseUnitName recognises the four templates' instances and returns the
+// ParseUnitName recognises the five templates' instances and returns the
// role and the ESCAPED identifier (systemd reports names escaped). Anything
// else, including a template with no instance, is not ours.
func ParseUnitName(unit string) (Role, string, bool) {
@@ -232,7 +241,7 @@ func ParseUnitName(unit string) (Role, string, bool) {
for _, p := range []struct {
prefix string
role Role
- }{{UnitPrefixSeed, RoleSeed}, {UnitPrefixSidecar, RoleSidecar}, {UnitPrefixCmd, RoleCmd}, {UnitPrefixRun, RoleRun}} {
+ }{{UnitPrefixSeed, RoleSeed}, {UnitPrefixSidecar, RoleSidecar}, {UnitPrefixCmd, RoleCmd}, {UnitPrefixWorker, RoleWorker}, {UnitPrefixRun, RoleRun}} {
if strings.HasPrefix(body, p.prefix) {
esc := strings.TrimPrefix(body, p.prefix)
if esc == "" {
diff --git a/internal/instancectl/names_test.go b/internal/instancectl/names_test.go
index 50b33f0f8..9f12cc697 100644
--- a/internal/instancectl/names_test.go
+++ b/internal/instancectl/names_test.go
@@ -53,8 +53,48 @@ func TestUnitAndSliceNamesAreKeyedByEscapedInstanceID(t *testing.T) {
}
}
+// Sidecar isolation D1 (#370): the fifth original role, worker, has its
+// own template, carlos-instance-worker@, keyed by the worker's own host
+// (a worker IS its host), in that host's slice. The sidecar role and its
+// template are untouched.
+func TestParseUnitNameWorker(t *testing.T) {
+ host := "bill-2.example.com"
+ esc := `bill\x2d2.example.com`
+ if UnitPrefixWorker != "carlos-instance-worker@" {
+ t.Fatalf("UnitPrefixWorker = %q", UnitPrefixWorker)
+ }
+ if got := UnitName(RoleWorker, host); got != "carlos-instance-worker@"+esc+".service" {
+ t.Errorf("worker unit = %q", got)
+ }
+ if got := UnitName(RoleWorker, host); got != UnitPrefixWorker+Escape(provision.InstanceID(host))+".service" {
+ t.Errorf("worker unit = %q is not keyed by the escaped instance id", got)
+ }
+ gotRole, gotEsc, ok := ParseUnitName("carlos-instance-worker@" + esc + ".service")
+ if !ok || gotRole != RoleWorker || gotEsc != esc {
+ t.Errorf("ParseUnitName(worker unit) = %q %q %v", gotRole, gotEsc, ok)
+ }
+ if _, _, ok := ParseUnitName("carlos-instance-worker@.service"); ok {
+ t.Error("a worker template with no instance was accepted")
+ }
+ if r, ok := ParseRole("worker"); !ok || r != RoleWorker || string(RoleWorker) != "worker" {
+ t.Errorf("ParseRole(worker) = %q %v", r, ok)
+ }
+ if got := UnitName(RoleSidecar, host); got != "carlos-instance-sidecar@"+esc+".service" {
+ t.Errorf("the sidecar unit changed: %q", got)
+ }
+ if got := SliceName(host); got != "carlos-instances-"+esc+".slice" {
+ t.Errorf("slice = %q, want the host's slice unchanged", got)
+ }
+ if got := LaunchFileName(RoleWorker); got != "launch.worker.json" {
+ t.Errorf("LaunchFileName(worker) = %q", got)
+ }
+ if got := ExitRecordName(RoleWorker, "abc123"); got != "exit.worker.abc123.json" {
+ t.Errorf("ExitRecordName(worker) = %q", got)
+ }
+}
+
func TestParseUnitNameRoundTrips(t *testing.T) {
- for _, role := range []Role{RoleRun, RoleSeed, RoleSidecar, RoleCmd} {
+ for _, role := range []Role{RoleRun, RoleSeed, RoleSidecar, RoleCmd, RoleWorker} {
u := UnitName(role, "bill-2.example.com")
gotRole, esc, ok := ParseUnitName(u)
if !ok || gotRole != role || esc != `bill\x2d2.example.com` {
@@ -72,12 +112,12 @@ func TestParseUnitNameRoundTrips(t *testing.T) {
}
func TestParseRole(t *testing.T) {
- for _, s := range []string{"run", "seed", "sidecar", "cmd"} {
+ for _, s := range []string{"run", "seed", "sidecar", "cmd", "worker"} {
if r, ok := ParseRole(s); !ok || string(r) != s {
t.Errorf("ParseRole(%q) = %q %v", s, r, ok)
}
}
- for _, s := range []string{"", "Run", "exec", "run ", "Cmd", "cmd-err"} {
+ for _, s := range []string{"", "Run", "exec", "run ", "Cmd", "cmd-err", "Worker", "workers"} {
if _, ok := ParseRole(s); ok {
t.Errorf("ParseRole(%q) accepted", s)
}
diff --git a/internal/instancectl/server.go b/internal/instancectl/server.go
index 6ccff14ff..25ed98a3c 100644
--- a/internal/instancectl/server.go
+++ b/internal/instancectl/server.go
@@ -28,7 +28,7 @@ import (
)
// Server is the root instance-control service (design §5). It does four
-// things and nothing else: starts, stops and sweeps the four templates'
+// things and nothing else: starts, stops and sweeps the five templates'
// instances, sets a host slice's memory tier before a start, runs the
// readiness probe, and answers /ready. Every systemctl argument it passes
// is built from a validated host by UnitName/SliceName; nothing from a
@@ -128,14 +128,14 @@ type Server struct {
genMu sync.Mutex
genStarting map[string]bool
- // Legacy run start attempts per host (legacystart.go, #292): the
- // starts in flight, the stops in progress, and each host's barrier,
- // the accept order of the latest stop's request. connSeq numbers
- // connections in accept order.
+ // Legacy start attempts per (role, host) (legacystart.go, #292; the
+ // worker role too, sidecar isolation D19): the starts in flight, the
+ // stops in progress, and each unit's barrier, the accept order of the
+ // latest stop's request. connSeq numbers connections in accept order.
legacyMu sync.Mutex
- legacyStarts map[string]map[*legacyAttempt]struct{}
- legacyStopping map[string]int
- legacyBarrier map[string]uint64
+ legacyStarts map[roleHost]map[*legacyAttempt]struct{}
+ legacyStopping map[roleHost]int
+ legacyBarrier map[roleHost]uint64
connSeq atomic.Uint64
// legacyJoin overrides legacyJoinBudget; tests shorten it.
legacyJoin time.Duration
@@ -368,7 +368,7 @@ func parseRequest(r *http.Request, v verbShape) (request, error) {
if len(v.roles) > 0 {
role, ok := ParseRole(q.Get("role"))
if !ok {
- return request{}, fmt.Errorf("role %q is not run, seed, sidecar or cmd", q.Get("role"))
+ return request{}, fmt.Errorf("role %q is not run, seed, sidecar, cmd or worker", q.Get("role"))
}
allowed := false
for _, r := range v.roles {
@@ -418,12 +418,14 @@ func parseRequest(r *http.Request, v verbShape) (request, error) {
if x := q.Get("exe"); x != "" {
// A generation start; the legacy sidecar (zero-downtime spec
// §7.5: it runs its instance's serving artifact); a run's
- // command, which runs that same artifact; or the original
+ // command, which runs that same artifact; the original
// template's run unit and the seed unit, which run the build a
- // cold wake recorded (settings spec §4.2). Every one is
- // validated before anything is laid out (refuseStagedExe).
- if req.gen == 0 && !v.seed && !v.cmd && req.role != RoleRun && req.role != RoleSidecar {
- return request{}, errors.New("exe is only for a run, seed or sidecar unit, a run's command, or a generation start")
+ // cold wake recorded (settings spec §4.2); or a worker unit,
+ // which always runs the staged build its wake resolved
+ // (sidecar isolation D5). Every one is validated before
+ // anything is laid out (refuseStagedExe).
+ if req.gen == 0 && !v.seed && !v.cmd && req.role != RoleRun && req.role != RoleSidecar && req.role != RoleWorker {
+ return request{}, errors.New("exe is only for a run, seed, sidecar or worker unit, a run's command, or a generation start")
}
req.exe = StagedExe{Path: x, Version: q.Get("version"), Digest: q.Get("digest")}
} else {
@@ -435,7 +437,7 @@ func parseRequest(r *http.Request, v verbShape) (request, error) {
// setTier writes the whole tier onto the host slice in one call: the
// memory caps, and the CPU and IO shares that follow from the same tier
-// (#214 item 1). Seed, instance and sidecar share all of it.
+// (#214 item 1). Seed, instance, sidecar and worker share all of it.
func (s *Server) setTier(ctx context.Context, host string, tier cgroup.Limit) error {
return sliceTier(ctx, s.Systemctl, host, tier)
}
@@ -459,20 +461,23 @@ func writeJSON(w http.ResponseWriter, status int, v any) {
}
func (s *Server) handleStart(w http.ResponseWriter, r *http.Request) {
- req, err := parseRequest(r, verbShape{roles: []Role{RoleRun, RoleSidecar}, tier: true, gen: true, exe: true})
+ req, err := parseRequest(r, verbShape{roles: []Role{RoleRun, RoleSidecar, RoleWorker}, tier: true, gen: true, exe: true})
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if req.gen != 0 {
+ // parseRequest admits a generation for the run role only; a worker
+ // has one unit per host and never a generation (D1).
s.startGen(w, r, req)
return
}
ctx := r.Context()
- if req.role == RoleRun {
- // An attempt a /stop of this host can cancel and join, and that a
- // stop which has already run refuses (legacystart.go, #292).
- attempt, finish, err := s.beginLegacyStart(ctx, req.host, requestSeq(ctx))
+ if fencedRole(req.role) {
+ // An attempt a /stop of this unit can cancel and join, and that a
+ // stop which has already run refuses (legacystart.go, #292; the
+ // worker unit too, sidecar isolation D19).
+ attempt, finish, err := s.beginLegacyStart(ctx, req.role, req.host, requestSeq(ctx))
if err != nil {
http.Error(w, "start: "+err.Error(), http.StatusConflict)
return
@@ -481,13 +486,17 @@ func (s *Server) handleStart(w http.ResponseWriter, r *http.Request) {
ctx = attempt
}
unit := UnitName(req.role, req.host)
- // A sidecar naming its generation's staged build, or a run unit naming
- // the build its cold wake recorded, is validated before anything is
- // laid out, under the generation rule (validateStagedExe).
+ // A sidecar naming its generation's staged build, a run unit naming
+ // the build its cold wake recorded, or a worker naming the build its
+ // wake resolved, is validated before anything is laid out, under the
+ // generation rule (validateStagedExe, keyed by the request's host: for
+ // a worker that is the worker host's own app).
if s.refuseStagedExe(w, req, unit) {
return
}
if req.role == RoleRun {
+ // The claim launch snapshot belongs to the run unit alone; a
+ // worker has none.
if err := s.clearLegacyClaimLaunch(ctx, req.host); err != nil {
http.Error(w, "start: "+err.Error(), http.StatusBadGateway)
return
@@ -507,15 +516,17 @@ func (s *Server) handleStart(w http.ResponseWriter, r *http.Request) {
// alone keeps the right tier too. A start without exe (a live-link
// sidecar) sets the tier as before. The exemption is the sidecar's
// alone: a run unit naming its recorded build starts a cold instance,
- // and sets its tier as a run start always has.
+ // and sets its tier as a run start always has; a worker's slice is its
+ // own (its host has no run unit), so a worker start always sets the
+ // tier too (sidecar isolation D1).
if req.role == RoleSidecar && req.exe.Path != "" && s.slicePopulated(req.host) {
s.logf("instance-control: %s: slice populated, leaving its limit to the generation owner", req.host)
} else if err := s.setTier(ctx, req.host, req.tier); err != nil {
http.Error(w, "set-property: "+err.Error(), http.StatusBadGateway)
return
}
- // Never submitted once cancelled (#292): a /stop of this host cancels
- // the attempt before it joins it, and the client may have gone.
+ // Never submitted once cancelled (#292, D19): a /stop of this unit
+ // cancels the attempt before it joins it, and the client may have gone.
if err := ctx.Err(); err != nil {
http.Error(w, "start: cancelled before it was submitted: "+err.Error(), http.StatusConflict)
return
@@ -658,7 +669,7 @@ func (s *Server) stopUnit(ctx context.Context, unit string) {
}
func (s *Server) handleStop(w http.ResponseWriter, r *http.Request) {
- req, err := parseRequest(r, verbShape{roles: []Role{RoleRun, RoleSeed, RoleSidecar, RoleCmd}, gen: true, kill: true})
+ req, err := parseRequest(r, verbShape{roles: []Role{RoleRun, RoleSeed, RoleSidecar, RoleCmd, RoleWorker}, gen: true, kill: true})
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
@@ -667,13 +678,15 @@ func (s *Server) handleStop(w http.ResponseWriter, r *http.Request) {
s.stopGen(w, r, req)
return
}
- if req.role == RoleRun {
- // Every start of this host's run unit in flight is cancelled and
- // joined first, and earlier requests' starts are refused from here
- // on (legacystart.go, #292): once this answers, no start from an
- // earlier request can still be submitted, and the stop below
- // replaces any job a joined start had submitted.
- release, err := s.fenceLegacyStarts(r.Context(), req.host)
+ if fencedRole(req.role) {
+ // Every start of this unit (the host's run unit, or its worker
+ // unit, D19) in flight is cancelled and joined first, and earlier
+ // requests' starts are refused from here on (legacystart.go,
+ // #292): once this answers, no start from an earlier request can
+ // still be submitted, and the stop below replaces any job a
+ // joined start had submitted. The fence is the unit's own: a
+ // worker stop fences no run start of the host, nor the reverse.
+ release, err := s.fenceLegacyStarts(r.Context(), req.role, req.host)
defer release()
if err != nil {
// Stopped anyway, but not proven final: a start may follow.
@@ -699,7 +712,7 @@ func (s *Server) handleStop(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
- req, err := parseRequest(r, verbShape{roles: []Role{RoleRun, RoleSeed, RoleSidecar, RoleCmd}, gen: true})
+ req, err := parseRequest(r, verbShape{roles: []Role{RoleRun, RoleSeed, RoleSidecar, RoleCmd, RoleWorker}, gen: true})
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
@@ -715,7 +728,7 @@ func (s *Server) handleReset(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
-// handleSweep stops every instance of the four templates that is not
+// handleSweep stops every instance of the five templates that is not
// inactive or has a job, keyed on the names systemd reports, so a unit
// whose identifier is hashed and whose row is gone is still stopped
// (design §6.7, codex R7-1).
@@ -723,7 +736,7 @@ func (s *Server) handleSweep(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
targets := map[string]bool{}
out, err := s.Systemctl(ctx, "list-units", "--all", "--plain", "--no-legend", "--no-pager",
- UnitPrefixRun+"*.service", UnitPrefixSeed+"*.service", UnitPrefixSidecar+"*.service", UnitPrefixCmd+"*.service", UnitPrefixGen+"*.service")
+ UnitPrefixRun+"*.service", UnitPrefixSeed+"*.service", UnitPrefixSidecar+"*.service", UnitPrefixCmd+"*.service", UnitPrefixWorker+"*.service", UnitPrefixGen+"*.service")
if err != nil {
http.Error(w, "list-units: "+err.Error(), http.StatusBadGateway)
return
diff --git a/internal/instancectl/server_test.go b/internal/instancectl/server_test.go
index c2e12c2b3..dd2e313bf 100644
--- a/internal/instancectl/server_test.go
+++ b/internal/instancectl/server_test.go
@@ -708,6 +708,92 @@ func TestReadyRefusesWhenTheRunningBinaryIsNotTheInstalledOne(t *testing.T) {
}
}
+// Sidecar isolation D1 (#370): a worker start on the shared verbs takes
+// the run role's tier rule. A worker's slice is its own, so the tier is
+// always set, and the start is the one systemd call sequence the run unit
+// gets: set-property, start, show.
+func TestStartWorkerSetsTier(t *testing.T) {
+ ctl := &fakeCtl{answers: map[string]string{"show -p InvocationID --value": "w1\n"}}
+ _, c := newServer(t, ctl)
+ resp := post(t, c, "/start?host=bill-2.example.com&role=worker&memory_max=536870912")
+ if resp.StatusCode != 200 {
+ t.Fatalf("status %d", resp.StatusCode)
+ }
+ var out StartResponse
+ if err := json.NewDecoder(resp.Body).Decode(&out); err != nil || out.InvocationID != "w1" {
+ t.Errorf("response = %+v, %v", out, err)
+ }
+ want := "set-property --runtime carlos-instances-bill\\x2d2.example.com.slice MemoryMax=536870912 MemoryHigh=483183819 MemorySwapMax=536870912 CPUWeight=100 CPUQuota=100% IOWeight=100\n" +
+ "start carlos-instance-worker@bill\\x2d2.example.com.service\n" +
+ "show -p InvocationID --value carlos-instance-worker@bill\\x2d2.example.com.service"
+ if got := ctl.joined(); got != want {
+ t.Errorf("systemctl calls:\n%s\nwant:\n%s", got, want)
+ }
+}
+
+// A worker has one unit per host and no generation units (D1): /start
+// with the worker role and a generation is 400, before systemctl is asked
+// anything; so are the generation-only parameters on its stop.
+func TestStartWorkerRefusesGen(t *testing.T) {
+ ctl := &fakeCtl{}
+ _, c := newServer(t, ctl, func(s *Server) { s.DropInDir = t.TempDir() })
+ for _, q := range []string{
+ "/start?host=h.example.com&role=worker&memory_max=536870912&gen=1",
+ "/start?host=h.example.com&role=worker&memory_max=536870912&gen=7",
+ "/stop?host=h.example.com&role=worker&gen=1",
+ "/stop?host=h.example.com&role=worker&kill=1",
+ "/reset?host=h.example.com&role=worker&gen=1",
+ } {
+ resp := post(t, c, q)
+ resp.Body.Close()
+ if resp.StatusCode != 400 {
+ t.Errorf("%s: status %d, want 400", q, resp.StatusCode)
+ }
+ }
+ if ctl.joined() != "" {
+ t.Errorf("systemctl was called: %s", ctl.joined())
+ }
+}
+
+// The worker role is admitted on /stop, /reset and /sweep (D1): its unit
+// is stopped and reset by name, and the sweep lists and stops the worker
+// template's instances as it does the other four.
+func TestStopResetAndSweepCoverTheWorkerRole(t *testing.T) {
+ ctl := &fakeCtl{answers: map[string]string{
+ "list-units": "carlos-instance-worker@a.example.com.service loaded active running carlos exec-backed instance a.example.com (worker)\n",
+ "show -p ActiveState --value": "inactive\n",
+ }}
+ s, c := newServer(t, ctl)
+ s.cgroupWaitBudget = time.Second
+ if resp := post(t, c, "/stop?host=a.example.com&role=worker"); resp.StatusCode != http.StatusNoContent {
+ t.Fatalf("stop status %d", resp.StatusCode)
+ }
+ if resp := post(t, c, "/reset?host=a.example.com&role=worker"); resp.StatusCode/100 != 2 {
+ t.Fatalf("reset status %d", resp.StatusCode)
+ }
+ resp := post(t, c, "/sweep")
+ if resp.StatusCode != http.StatusOK {
+ t.Fatalf("sweep status %d", resp.StatusCode)
+ }
+ var swept SweepResponse
+ if err := json.NewDecoder(resp.Body).Decode(&swept); err != nil {
+ t.Fatal(err)
+ }
+ if len(swept.Units) != 1 || swept.Units[0] != "carlos-instance-worker@a.example.com.service" {
+ t.Errorf("swept = %v, want the listed worker unit", swept.Units)
+ }
+ calls := ctl.joined()
+ for _, want := range []string{
+ "stop carlos-instance-worker@a.example.com.service",
+ "reset-failed carlos-instance-worker@a.example.com.service",
+ "carlos-instance-worker@*.service",
+ } {
+ if !strings.Contains(calls, want) {
+ t.Errorf("missing %q in\n%s", want, calls)
+ }
+ }
+}
+
func TestStopResetAndSweepCoverTheCmdRole(t *testing.T) {
ctl := &fakeCtl{answers: map[string]string{
"list-units": "carlos-instance-cmd@a.example.com.service loaded active exited carlos exec-backed instance a.example.com (cmd)\n",
diff --git a/internal/instancectl/stagedexe_linux_test.go b/internal/instancectl/stagedexe_linux_test.go
index da863eb5f..96f21a192 100644
--- a/internal/instancectl/stagedexe_linux_test.go
+++ b/internal/instancectl/stagedexe_linux_test.go
@@ -245,6 +245,93 @@ func TestTheRunUnitBindsAValidatedStagedExe(t *testing.T) {
}
}
+// Sidecar isolation D1/D5 (#370): a worker start always names the staged
+// build the agent resolved, and root validates it with validateStagedExe
+// against the WORKER HOST's own app (HostApp is keyed by host), as it does
+// a run unit's. The validated build is what the host's app mount points
+// at; a wrong digest or another app's build is 403 before systemd or the
+// mount is touched. The tier is always set, populated slice or not: the
+// populated-slice exemption is the legacy sidecar's alone.
+func TestWorkerStartValidatesStagedExe(t *testing.T) {
+ const worker = "worker-1.example.com"
+ opt, exeRoot := t.TempDir(), t.TempDir()
+ staged := release.VersionPath(opt, release.OpsAccount, "v2", "blog")
+ digest := writeStaged(t, staged, "worker build v2")
+ shop := release.VersionPath(opt, release.OpsAccount, "v2", "shop")
+ shopDigest := writeStaged(t, shop, "shop build v2")
+ repointLiveLink(t, opt, worker, staged)
+ ctl := &fakeCtl{answers: map[string]string{"show -p InvocationID --value": "w1\n"}}
+ var cgroupRoot string
+ asked := map[string]int{}
+ _, sock := serveOnSocket(t, ctl, func(s *Server) {
+ s.DropInDir, s.ExeRoot, s.OptDir = t.TempDir(), exeRoot, opt
+ cgroupRoot = s.CgroupRoot
+ s.HostApp = func(host string) (string, string, error) {
+ asked[host]++
+ switch host {
+ case worker:
+ return release.OpsAccount, "blog", nil
+ case "shop.example.com":
+ return release.OpsAccount, "shop", nil
+ }
+ return "", "", errors.New("unknown host")
+ }
+ })
+ c := NewClient(sock)
+ x := StagedExe{Path: staged, Version: "v2", Digest: digest}
+ app := filepath.Join(exeRoot, provision.InstanceID(worker), "app")
+
+ if _, err := c.StartExe(context.Background(), worker, RoleWorker, cgroup.TierDefault.MemoryMax, x); err != nil {
+ t.Fatalf("the worker host's own staged build was refused: %v", err)
+ }
+ if asked[worker] == 0 {
+ t.Fatal("the registry was not asked about the worker host")
+ }
+ if !ctlCalled(ctl, "start "+UnitName(RoleWorker, worker)) {
+ t.Fatalf("the worker unit was not started:\n%s", ctl.joined())
+ }
+ if !ctlCalled(ctl, "set-property --runtime "+SliceName(worker)) {
+ t.Fatalf("a worker start set no tier:\n%s", ctl.joined())
+ }
+ if got, err := os.Readlink(app); err != nil || got != staged {
+ t.Fatalf("the worker's app mount = %q (%v), want the staged %q", got, err, staged)
+ }
+
+ for name, bad := range map[string]StagedExe{
+ "a wrong digest": {Path: staged, Version: "v2", Digest: strings.Repeat("0", 64)},
+ "another app's build": {Path: shop, Version: "v2", Digest: shopDigest},
+ "a path outside": {Path: "/etc/passwd", Version: "v2", Digest: digest},
+ } {
+ ctl.reset()
+ if _, err := c.StartExe(context.Background(), worker, RoleWorker, cgroup.TierDefault.MemoryMax, bad); !errors.Is(err, ErrStagedExe) {
+ t.Errorf("%s: %v, want ErrStagedExe", name, err)
+ }
+ if ctl.joined() != "" {
+ t.Errorf("%s: a refused start reached systemctl:\n%s", name, ctl.joined())
+ }
+ if got, _ := os.Readlink(app); got != staged {
+ t.Errorf("%s: a refused start moved the mount to %q", name, got)
+ }
+ }
+
+ // A populated slice: the worker still gets its tier (the exemption at
+ // server.go's sidecar branch is sidecar-only).
+ events := filepath.Join(cgroupRoot, parentSlices, SliceName(worker), "cgroup.events")
+ if err := os.MkdirAll(filepath.Dir(events), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(events, []byte("populated 1\nfrozen 0\n"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ ctl.reset()
+ if _, err := c.StartExe(context.Background(), worker, RoleWorker, cgroup.TierDefault.MemoryMax, x); err != nil {
+ t.Fatal(err)
+ }
+ if !ctlCalled(ctl, "set-property --runtime "+SliceName(worker)) {
+ t.Fatal("a staged worker start on a populated slice set no tier")
+ }
+}
+
// The seed unit binds the recorded staged file the same way, through
// /seed, which names no role. A wrong digest is 403 before anything runs,
// and a seed that names no executable binds the live link, as today.
diff --git a/internal/instanceexec/exec.go b/internal/instanceexec/exec.go
index 4e0391df8..4e76adec5 100644
--- a/internal/instanceexec/exec.go
+++ b/internal/instanceexec/exec.go
@@ -41,7 +41,7 @@ func dataDir() string {
// RunExec is `carlos instance-exec <role> <iid>`.
func RunExec(args []string) {
fs := flag.NewFlagSet("instance-exec", flag.ExitOnError)
- fs.Usage = cliflag.Usage(fs, "usage: carlos instance-exec <run|seed|sidecar|cmd> <instance-id>")
+ fs.Usage = cliflag.Usage(fs, "usage: carlos instance-exec <run|seed|sidecar|cmd|worker> <instance-id>")
claimSnapshot := fs.String("claim-reservation", "", "root launch reservation")
_ = fs.Parse(args)
if fs.NArg() != 2 {
@@ -50,7 +50,7 @@ func RunExec(args []string) {
}
role, ok := instancectl.ParseRole(fs.Arg(0))
if !ok {
- log.Fatalf("instance-exec: role %q is not run, seed, sidecar or cmd", fs.Arg(0))
+ log.Fatalf("instance-exec: role %q is not run, seed, sidecar, cmd or worker", fs.Arg(0))
}
// The tenant is scoped (#178, see ScopeAbstractUnix) and runs under
// the trampoline as its init (#126, see init_linux.go).
diff --git a/internal/instanceexec/exec_test.go b/internal/instanceexec/exec_test.go
index 3d94b51db..c2fa1d85f 100644
--- a/internal/instanceexec/exec_test.go
+++ b/internal/instanceexec/exec_test.go
@@ -61,6 +61,36 @@ func TestExecLaunchExecsExactlyWhatTheAgentWrote(t *testing.T) {
}
}
+// Sidecar isolation D1 (#370): the trampoline accepts the worker role and
+// reads launch.worker.json, keyed by the worker's own host. A worker has
+// no generations: "<iid>:<n>" under the worker role is refused as it is
+// for the seed and sidecar roles.
+func TestExecLaunchRunsTheWorkerRolesLaunchFile(t *testing.T) {
+ data := t.TempDir()
+ host := "worker-1.example.com"
+ iid := provision.InstanceID(host)
+ dir := filepath.Join(data, "instances", iid)
+ writeLaunch(t, dir, instancectl.RoleWorker, instancectl.Launch{
+ Schema: 1, Host: host,
+ Argv: []string{"/opt/carlos/live/" + host, "worker", "run"},
+ Env: []string{"CARLOS_SIDECAR_DB=/data/instances/" + host + "/" + host + ".db"},
+ })
+ var c captured
+ if err := execLaunch(data, instancectl.RoleWorker, iid, c.exec); err != nil {
+ t.Fatalf("execLaunch(worker): %v", err)
+ }
+ if c.calls != 1 || c.argv0 != "/opt/carlos/live/"+host || len(c.argv) != 3 || c.argv[1] != "worker" {
+ t.Errorf("exec = %+v", c)
+ }
+ if role, ok := instancectl.ParseRole("worker"); !ok || role != instancectl.RoleWorker {
+ t.Errorf("ParseRole(worker) = %q %v", role, ok)
+ }
+ var gen captured
+ if err := execLaunch(data, instancectl.RoleWorker, iid+":7", gen.exec); err == nil || gen.calls != 0 {
+ t.Errorf("a worker generation: %v after %d execs; want a refusal", err, gen.calls)
+ }
+}
+
func TestExecLaunchRefusesAnArgv0OutsideTheLiveDir(t *testing.T) {
data := t.TempDir()
iid := "h.example.com"
diff --git a/internal/instanceexec/exit.go b/internal/instanceexec/exit.go
index 9c5aa6f2d..3da028c71 100644
--- a/internal/instanceexec/exit.go
+++ b/internal/instanceexec/exit.go
@@ -32,7 +32,7 @@ var invocationShape = regexp.MustCompile(`^[0-9a-f]{32}$`)
func RunExit(args []string) {
fs := flag.NewFlagSet("instance-exit", flag.ExitOnError)
runDir := fs.String("run-dir", provision.SocketDir, "run directory holding instances/<id>")
- fs.Usage = cliflag.Usage(fs, "usage: carlos instance-exit <run|seed|sidecar|cmd> <instance-id>")
+ fs.Usage = cliflag.Usage(fs, "usage: carlos instance-exit <run|seed|sidecar|cmd|worker> <instance-id>")
_ = fs.Parse(args)
if fs.NArg() != 2 {
fs.Usage()
@@ -40,7 +40,7 @@ func RunExit(args []string) {
}
role, ok := instancectl.ParseRole(fs.Arg(0))
if !ok {
- log.Printf("instance-exit: role %q is not run, seed, sidecar or cmd", fs.Arg(0))
+ log.Printf("instance-exit: role %q is not run, seed, sidecar, cmd or worker", fs.Arg(0))
return
}
if err := writeExitRecord(*runDir, role, fs.Arg(1), os.Getenv, time.Now()); err != nil {
diff --git a/internal/instanceexec/exit_test.go b/internal/instanceexec/exit_test.go
index 217f62bfe..216fdf5a3 100644
--- a/internal/instanceexec/exit_test.go
+++ b/internal/instanceexec/exit_test.go
@@ -103,4 +103,15 @@ func TestWriteExitRecordKeysAGenerationRecordByGeneration(t *testing.T) {
if err := writeExitRecord(run, instancectl.RoleSidecar, iid+":7", env, now); err == nil {
t.Error("a sidecar generation was accepted")
}
+ // Sidecar isolation D1 (#370): the worker role writes exit.worker.<inv>
+ // and, having no generations, refuses "<iid>:<n>".
+ if err := writeExitRecord(run, instancectl.RoleWorker, iid, env, now); err != nil {
+ t.Fatalf("writeExitRecord(worker): %v", err)
+ }
+ if _, err := os.Stat(filepath.Join(run, "instances", iid, "exit.worker.deadbeefdeadbeefdeadbeefdeadbeef.json")); err != nil {
+ t.Errorf("worker record missing: %v", err)
+ }
+ if err := writeExitRecord(run, instancectl.RoleWorker, iid+":7", env, now); err == nil {
+ t.Error("a worker generation was accepted")
+ }
}