carlos / platform Public

Static sites honour a _redirects file

A static site can now ship a `_redirects` file at the top of its tree, in
the format Netlify introduced and Cloudflare Pages adopted: one
`source destination [status]` rule per line. Sources are paths or
http(s)://host URLs (a host rule matches only that host, for www -> apex);
`:name` captures a segment and a final `*` the rest, as `:splat`. 301
(default), 302, 303, 307 and 308 redirect; 200 serves another file of the
tree at the request's URL (SPA fallback) and 404 serves one as the
not-found page. A file at the path wins unless the status ends in `!`.

`carlos ship`/`deploy` refuse a file with a line the edge cannot use
(PackDir), naming every such line; Netlify conditions and proxying to
other hosts are not supported. The edge parses the file once per version
swap and keeps the rules with that version's tree, never serves the file,
skips a redirect to the URL being requested, and cleans every Location so
no capture can make it protocol-relative. A site without the file is
served exactly as before. Documented under carlos deploy in
docs/site/cli.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by james@keymail.dev pushed by james@keymail.dev 3394c0ad1fbad8cc2735257dee68c4a98433a1cd parent 5f341f3
4 files changed, +911 −21
  • docs/site/cli.md +38 −0
  • internal/edge/redirects.go +418 −0
  • internal/edge/redirects_test.go +319 −0
  • internal/edge/static.go +136 −21

CI failed — run details

CI log
=== gate ===
.amadan/test-gate.sh
go: downloading github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.68.0
go: downloading github.com/aws/aws-sdk-go-v2/service/costexplorer v1.67.5
go: downloading github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0
go: downloading github.com/aws/aws-sdk-go-v2 v1.44.0
go: downloading github.com/aws/smithy-go v1.28.1
go: downloading github.com/superfly/ltx v0.5.1
go: downloading golang.org/x/net v0.56.0
go: downloading modernc.org/sqlite v1.55.0
go: downloading github.com/aws/aws-sdk-go-v2/config v1.32.6
go: downloading github.com/aws/aws-sdk-go-v2/service/sns v1.43.0
go: downloading github.com/benbjohnson/litestream v0.5.14
go: downloading golang.org/x/crypto v0.53.0
go: downloading github.com/aws/aws-sdk-go-v2/service/acm v1.45.0
go: downloading github.com/aws/aws-sdk-go-v2/service/ec2 v1.324.0
go: downloading github.com/aws/aws-sdk-go-v2/service/iam v1.58.2
go: downloading github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.37.0
go: downloading github.com/aws/aws-sdk-go-v2/service/route53 v1.64.1
go: downloading github.com/aws/aws-sdk-go-v2/service/sts v1.45.6
go: downloading github.com/aws/aws-sdk-go-v2/service/autoscaling v1.74.0
go: downloading golang.org/x/sys v0.46.0
go: downloading github.com/aws/aws-sdk-go-v2/credentials v1.19.6
go: downloading github.com/aws/aws-sdk-go-v2/service/route53 v1.64.1
go: downloading github.com/aws/aws-sdk-go-v2/service/acm v1.45.0
go: downloading github.com/aws/aws-sdk-go-v2/service/ec2 v1.324.0
go: downloading github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0
go: downloading github.com/aws/aws-sdk-go-v2/service/iam v1.58.2
go: downloading modernc.org/sqlite v1.55.0
go: downloading github.com/superfly/ltx v0.5.1
go: downloading github.com/oschwald/maxminddb-golang v1.13.1
go: downloading github.com/benbjohnson/litestream v0.5.14
go: downloading github.com/klauspost/compress v1.18.0
go: downloading github.com/aws/aws-sdk-go-v2/service/ssm v1.74.0
go: downloading github.com/pion/logging v0.2.4
go: downloading github.com/pion/stun/v3 v3.0.1
go: downloading github.com/pion/turn/v4 v4.1.4
go: downloading github.com/pion/stun/v3 v3.0.1
go: downloading github.com/pion/turn/v4 v4.1.4
go: downloading github.com/aws/aws-sdk-go-v2/service/sesv2 v1.66.6
go: downloading github.com/aws/aws-sdk-go-v2/service/sesv2 v1.66.6
go: downloading github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.40
go: downloading github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.38
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.40
go: downloading github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16
go: downloading github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4
go: downloading github.com/aws/aws-sdk-go-v2/service/signin v1.0.4
go: downloading github.com/aws/aws-sdk-go-v2/service/sso v1.30.8
go: downloading github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12
go: downloading github.com/pion/randutil v0.1.0
go: downloading github.com/pion/transport/v4 v4.0.1
go: downloading github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.40
go: downloading github.com/pierrec/lz4/v4 v4.1.22
go: downloading github.com/pierrec/lz4/v4 v4.1.22
go: downloading github.com/wlynxg/anet v0.0.5
go: downloading github.com/wlynxg/anet v0.0.5
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.23
go: downloading github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14
go: downloading github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.23
go: downloading github.com/prometheus/client_golang v1.17.0
go: downloading golang.org/x/sync v0.21.0
go: downloading github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.18
go: downloading github.com/mattn/go-isatty v0.0.20
go: downloading github.com/lmittmann/tint v1.1.3
go: downloading github.com/prometheus/client_golang v1.17.0
go: downloading github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.18
go: downloading golang.org/x/sync v0.21.0
go: downloading github.com/lmittmann/tint v1.1.3
go: downloading github.com/mattn/go-isatty v0.0.20
go: downloading github.com/pion/transport/v3 v3.0.8
go: downloading github.com/pion/dtls/v3 v3.0.7
go: downloading github.com/pion/transport/v3 v3.0.8
go: downloading github.com/pion/dtls/v3 v3.0.7
go: downloading golang.org/x/text v0.38.0
go: downloading golang.org/x/text v0.38.0
go: downloading github.com/prometheus/client_model v0.5.0
go: downloading github.com/beorn7/perks v1.0.1
go: downloading github.com/cespare/xxhash/v2 v2.3.0
go: downloading github.com/prometheus/procfs v0.12.0
go: downloading google.golang.org/protobuf v1.36.10
go: downloading github.com/prometheus/common v0.45.0
go: downloading github.com/beorn7/perks v1.0.1
go: downloading github.com/prometheus/client_model v0.5.0
go: downloading github.com/prometheus/common v0.45.0
go: downloading github.com/cespare/xxhash/v2 v2.3.0
go: downloading github.com/prometheus/procfs v0.12.0
go: downloading google.golang.org/protobuf v1.36.10
go: downloading github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0
go: downloading github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0
go: downloading modernc.org/libc v1.74.1
go: downloading modernc.org/libc v1.74.1
go: downloading modernc.org/memory v1.11.0
go: downloading github.com/dustin/go-humanize v1.0.1
go: downloading github.com/google/uuid v1.6.0
go: downloading modernc.org/mathutil v1.7.1
go: downloading modernc.org/memory v1.11.0
go: downloading github.com/dustin/go-humanize v1.0.1
go: downloading modernc.org/mathutil v1.7.1
go: downloading github.com/google/uuid v1.6.0
go: downloading github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec
=== launchctx (3 s)
ok  	amadan.net/carlos/platform.git/launchctx	0.011s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
=== vet (45 s)
go: downloading github.com/aws/aws-sdk-go-v2/service/route53 v1.64.1
go: downloading github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0
go: downloading modernc.org/sqlite v1.55.0
go: downloading github.com/superfly/ltx v0.5.1
go: downloading github.com/benbjohnson/litestream v0.5.14
go: downloading github.com/oschwald/maxminddb-golang v1.13.1
go: downloading github.com/aws/aws-sdk-go-v2/service/ec2 v1.324.0
go: downloading github.com/aws/aws-sdk-go-v2/service/acm v1.45.0
go: downloading github.com/aws/aws-sdk-go-v2/service/iam v1.58.2
go: downloading github.com/aws/aws-sdk-go-v2/service/ssm v1.74.0
go: downloading github.com/aws/aws-sdk-go-v2/service/sesv2 v1.66.6
go: downloading golang.org/x/tools v0.47.0
go: downloading github.com/pion/stun/v3 v3.0.1
go: downloading github.com/klauspost/compress v1.18.0
go: downloading github.com/pierrec/lz4/v4 v4.1.22
go: downloading github.com/wlynxg/anet v0.0.5
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.23
go: downloading github.com/prometheus/client_golang v1.17.0
go: downloading golang.org/x/sync v0.21.0
go: downloading github.com/mattn/go-isatty v0.0.20
go: downloading github.com/lmittmann/tint v1.1.3
go: downloading github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.18
go: downloading github.com/pion/transport/v3 v3.0.8
go: downloading github.com/pion/dtls/v3 v3.0.7
go: downloading golang.org/x/text v0.38.0
go: downloading github.com/cespare/xxhash/v2 v2.3.0
go: downloading github.com/prometheus/client_model v0.5.0
go: downloading github.com/prometheus/procfs v0.12.0
go: downloading github.com/prometheus/common v0.45.0
go: downloading google.golang.org/protobuf v1.36.10
go: downloading github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0
go: downloading golang.org/x/mod v0.37.0
go: downloading modernc.org/libc v1.74.1
go: downloading modernc.org/memory v1.11.0
go: downloading github.com/google/uuid v1.6.0
go: downloading modernc.org/mathutil v1.7.1
go: downloading github.com/dustin/go-humanize v1.0.1
go: downloading github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec
=== gofmt (1 s)
gofmt -l .: clean
=== rest (186 s)
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	3.364s
ok  	github.com/carlosframework/platform/cmd/moveprobe	11.678s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.748s
ok  	github.com/carlosframework/platform/internal/activator	61.650s
ok  	github.com/carlosframework/platform/internal/agentsock	0.029s
ok  	github.com/carlosframework/platform/internal/alerting	0.005s
ok  	github.com/carlosframework/platform/internal/analytics	0.038s
ok  	github.com/carlosframework/platform/internal/appconfig	2.644s
ok  	github.com/carlosframework/platform/internal/apperr	0.101s
ok  	github.com/carlosframework/platform/internal/applog	0.203s
ok  	github.com/carlosframework/platform/internal/awsinv	0.007s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.012s
ok  	github.com/carlosframework/platform/internal/boxid	0.853s
?   	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.015s
ok  	github.com/carlosframework/platform/internal/caps	0.025s
ok  	github.com/carlosframework/platform/internal/certwarm	0.004s
ok  	github.com/carlosframework/platform/internal/cgroup	0.761s
ok  	github.com/carlosframework/platform/internal/changelog	0.406s
ok  	github.com/carlosframework/platform/internal/claimactivation	1.047s
ok  	github.com/carlosframework/platform/internal/clamd	0.058s
?   	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	6.507s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.022s
ok  	github.com/carlosframework/platform/internal/dbverify	0.007s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.007s
ok  	github.com/carlosframework/platform/internal/docsite	0.018s
ok  	github.com/carlosframework/platform/internal/domains	2.522s
ok  	github.com/carlosframework/platform/internal/doorkey	0.009s
ok  	github.com/carlosframework/platform/internal/economics	0.007s
ok  	github.com/carlosframework/platform/internal/edge	49.319s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.009s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.159s
ok  	github.com/carlosframework/platform/internal/gate	1.662s
ok  	github.com/carlosframework/platform/internal/genselect	0.031s
ok  	github.com/carlosframework/platform/internal/geo	0.023s
ok  	github.com/carlosframework/platform/internal/geoip	0.032s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.179s
ok  	github.com/carlosframework/platform/internal/host	137.614s
ok  	github.com/carlosframework/platform/internal/hostactors	6.351s
ok  	github.com/carlosframework/platform/internal/hostactors/awsmembership	0.008s
ok  	github.com/carlosframework/platform/internal/hostbox	3.254s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.741s
ok  	github.com/carlosframework/platform/internal/hostenv	0.005s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.008s
ok  	github.com/carlosframework/platform/internal/hostexec	5.691s
ok  	github.com/carlosframework/platform/internal/hostfence	0.055s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.603s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.018s
ok  	github.com/carlosframework/platform/internal/hostguard	1.030s
ok  	github.com/carlosframework/platform/internal/hostops	2.017s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.006s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.003s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.605s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.213s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.003s
ok  	github.com/carlosframework/platform/internal/infraview	0.002s
ok  	github.com/carlosframework/platform/internal/instancectl	5.228s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.841s
ok  	github.com/carlosframework/platform/internal/instancefs	0.007s
ok  	github.com/carlosframework/platform/internal/instancekey	0.002s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.024s
ok  	github.com/carlosframework/platform/internal/layout	1.751s
ok  	github.com/carlosframework/platform/internal/lbhealth	0.018s
ok  	github.com/carlosframework/platform/internal/lease	10.397s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.497s
ok  	github.com/carlosframework/platform/internal/ledger	0.030s
ok  	github.com/carlosframework/platform/internal/mail	0.233s
ok  	github.com/carlosframework/platform/internal/maintenance	0.091s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.590s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.116s
ok  	github.com/carlosframework/platform/internal/metrics	0.005s
ok  	github.com/carlosframework/platform/internal/moves	29.334s
ok  	github.com/carlosframework/platform/internal/moves/archive	1.035s
ok  	github.com/carlosframework/platform/internal/objstore	0.297s
ok  	github.com/carlosframework/platform/internal/ops	0.219s
ok  	github.com/carlosframework/platform/internal/park	0.258s
ok  	github.com/carlosframework/platform/internal/plans	0.005s
ok  	github.com/carlosframework/platform/internal/privatefile	0.004s
2026/10/22 15:22:14 provision: listing apps/bab/blog/instances/: list refused — its records are unseen this pass
2026/10/22 15:22:14 provision: listing apps/bab/: listdirs refused — its records are unseen this pass
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: listing apps/bac/tito/instances/: list refused — its records are unseen this pass
2026/10/22 15:22:14 provision: listing apps/bac/tito/instances/: list refused — its records are unseen this pass
2026/10/22 15:22:14 provision: skipping record apps/bac/tito/instances/one.example.com.json: unreadable: read refused
2026/10/22 15:22:14 provision: listing apps/bac/tito/instances/: list refused — its records are unseen this pass
2026/10/22 15:22:14 provision: an app's records could not be listed: they are skipped
2026/10/22 15:22:14 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:14 provision: remove retired instance route shop.example: provision: claim admission unavailable
2026/10/22 15:22:14 provision: remove retired instance route shop.example: provision: claim admission unavailable
2026/10/22 15:22:14 provision: remove retired sidecar route worker.sidecar.internal: provision: claim admission unavailable
2026/10/22 15:22:14 provision: remove retired sidecar route worker.sidecar.internal: provision: claim admission unavailable
2026/10/22 15:22:14 provision: claim install acme.go.tito.io: provision: claim admission unavailable
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: claim install acme.go.tito.io: registry: claim row ownership changed or unknown
2026/10/22 15:22:14 provision: claim install bot.eleven.bac.sidecar.internal: provision: claim admission unavailable
2026/10/22 15:22:14 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/22 15:22:14 provision: claim install bot.eleven.bac.sidecar.internal: registry: claim row ownership changed or unknown
2026/10/22 15:22:14 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/22 15:22:14 provision: acme.go.tito.io: not minting this pass: the box's layout intent could not be read: no sealed config
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 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/22 15:22:14 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/22 15:22:14 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/22 15:22:14 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/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 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/22 15:22:14 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/22 15:22:14 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: claim install acme.go.tito.io: registry: claim row ownership changed or unknown
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: claim install acme.go.tito.io: registry: claim row ownership changed or unknown
2026/10/22 15:22:14 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/22 15:22:14 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["run"])
2026/10/22 15:22:14 provision: claim install bot.eleven.bac.sidecar.internal: registry: claim row ownership changed or unknown
2026/10/22 15:22:14 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/22 15:22:14 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/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:14 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:14 provision: skipping record apps/bac/tito/instances/acme.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:14 provision: skipping record apps/bac/tito/instances/new.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:14 provision: not removing acme.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/22 15:22:14 provision: skipping record apps/bac/tito/instances/acme.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:14 provision: skipping record apps/bac/tito/instances/new.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:15 provision: minted instance route rb.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rc.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route re.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rf.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rg.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rj.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rl.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route ra.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rd.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rh.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route ri.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rk.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route ra.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rb.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rc.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route rd.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: skipping record apps/bac/tito/instances/rb.go.tito.io.json: read failed: read refused
2026/10/22 15:22:15 provision: skipping record apps/bac/tito/instances/rb.go.tito.io.json: read failed: read refused
2026/10/22 15:22:15 provision: minted instance route re.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.appi.example (app "appi" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.appa.example (app "appa" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.appb.example (app "appb" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.appe.example (app "appe" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.appf.example (app "appf" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.apph.example (app "apph" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.appj.example (app "appj" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.appc.example (app "appc" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.appd.example (app "appd" acct "bac")
2026/10/22 15:22:15 provision: minted instance route x.appg.example (app "appg" acct "bac")
2026/10/22 15:22:15 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/22 15:22:15 provision: minted instance route r0107.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0118.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0030.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0025.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0000.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0005.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0033.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0017.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0041.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0043.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0102.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0119.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0023.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0026.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0050.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0062.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0078.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0083.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0103.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0111.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0047.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0089.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0090.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0101.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0121.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0060.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0100.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0055.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0021.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0022.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0013.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0001.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0035.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0011.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0059.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0066.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0067.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0127.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0015.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0046.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0114.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0068.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0086.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0038.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0065.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0115.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0105.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0112.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0116.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0008.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0024.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0053.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0054.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0077.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0080.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0029.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0039.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0117.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0122.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0123.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0040.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0051.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0004.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0012.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0072.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0075.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0091.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0032.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0045.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0094.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0009.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0058.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0088.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0057.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0096.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0027.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0010.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0093.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0095.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0108.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0006.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0020.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0109.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0129.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0125.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0014.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0128.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0071.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0037.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0044.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0113.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0007.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0073.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0085.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0087.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0099.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0016.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0049.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0104.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0120.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0019.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0056.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0081.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0084.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0002.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0031.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0042.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0061.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0069.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0070.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0076.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0092.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0028.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0034.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0063.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0124.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0048.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0064.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0097.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0036.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0052.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0018.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0074.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0079.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0098.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0106.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0126.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0003.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0082.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:15 provision: minted instance route r0110.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 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/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route console.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted service route ha.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted service route svc.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route whole.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route shared.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: converged acme.go.tito.io channel stable -> edge
2026/10/22 15:22:18 provision: converged acme.go.tito.io channel edge -> canary/rehearsal
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: skipping record apps/bac/tito/instances/acme.go.tito.io.json: empty channel
2026/10/22 15:22:18 provision: not removing acme.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: converged acme.go.tito.io environment "" -> "staging"
2026/10/22 15:22:18 provision: converged acme.go.tito.io environment "staging" -> ""
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: converged acme.go.tito.io steering "" -> "latency"
2026/10/22 15:22:18 provision: converged acme.go.tito.io steering "latency" -> ""
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: converged acme.go.tito.io hibernate false -> true
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: converged acme.go.tito.io hibernate true -> false (replication "daily" cleared: only a hibernating route's rung is read)
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: converged acme.go.tito.io status-named false -> true
2026/10/22 15:22:18 provision: converged acme.go.tito.io status-named true -> false
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted service route ha.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 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/22 15:22:18 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/22 15:22:18 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/22 15:22:18 provision: minted service route whole.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 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/22 15:22:18 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/22 15:22:18 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/22 15:22:18 provision: minted service route whole.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: removed retired instance route acme.go.tito.io (record gone)
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: skipping accounts/bac/account.json: malformed account manifest: invalid character 'o' in literal null (expecting 'u')
2026/10/22 15:22:18 provision: removed retired instance route acme.go.tito.io (record gone)
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: manifest bac/tito: read failed: provision: manifest bac/tito: invalid character 'o' in literal null (expecting 'u')
2026/10/22 15:22:18 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/22 15:22:18 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/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 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/22 15:22:18 provision: not removing acme$evil.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/22 15:22:18 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/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 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/22 15:22:18 provision: not removing bad-service.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/22 15:22:18 provision: skipping record apps/bac/tito/instances/no-channel.go.tito.io.json: empty channel
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: skipping record apps/bac/tito/instances/no-channel.go.tito.io.json: empty channel
2026/10/22 15:22:18 provision: not removing no-channel.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/22 15:22:18 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:18 provision: skipping record apps/bac/tito/instances/acme.go.tito.io.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:18 provision: not removing acme.go.tito.io: its record exists but could not be read cleanly this pass
2026/10/22 15:22:18 provision: removed retired instance route ghost.go.tito.io (record gone)
2026/10/22 15:22:18 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/22 15:22:18 provision: minted instance route dup.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/22 15:22:19 provision: removed retired instance route acme.go.tito.io (app in the trash)
2026/10/22 15:22:19 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/22 15:22:19 provision: removed retired static route site.go.tito.io (app in the trash)
2026/10/22 15:22:19 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/22 15:22:19 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/22 15:22:19 provision: removed retired instance route acme.go.tito.io (app in the trash)
2026/10/22 15:22:19 provision: bac/tito is in the trash — not minting instance routes for it
2026/10/22 15:22:19 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/22 15:22:19 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/22 15:22:19 provision: converged acme.go.tito.io channel stable -> edge
2026/10/22 15:22:19 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/22 15:22:19 provision: converged acme.go.tito.io channel canary/rehearsal -> edge
2026/10/22 15:22:19 provision: converged acme.go.tito.io environment "staging" -> ""
2026/10/22 15:22:19 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/22 15:22:19 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/22 15:22:19 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/22 15:22:19 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/22 15:22:19 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/22 15:22:19 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/22 15:22:19 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/22 15:22:19 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/22 15:22:19 provision: removed retired instance route acme.go.tito.io (record gone)
2026/10/22 15:22:19 provision: dry-run: would mint instance route acme.go.tito.io (app "tito" acct "bac" channel "stable")
2026/10/22 15:22:19 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/22 15:22:19 provision: dry-run: would converge adopt.go.tito.io channel stable -> edge
2026/10/22 15:22:19 provision: dry-run: would converge adopt.go.tito.io environment "" -> "staging"
2026/10/22 15:22:19 provision: dry-run: would converge owned.go.tito.io channel stable -> edge
2026/10/22 15:22:19 provision: dry-run: would converge owned.go.tito.io environment "" -> "staging"
2026/10/22 15:22:19 provision: dry-run: would converge owned.go.tito.io hibernate false -> true
2026/10/22 15:22:19 provision: dry-run: would converge owned.go.tito.io seed
2026/10/22 15:22:19 provision: dry-run: would remove retired instance route gone.go.tito.io (record gone)
2026/10/22 15:22:19 provision: converged owned.go.tito.io channel stable -> edge
2026/10/22 15:22:19 provision: converged owned.go.tito.io environment "" -> "staging"
2026/10/22 15:22:19 provision: converged owned.go.tito.io hibernate false -> true
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: adopted existing route adopt.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/22 15:22:19 provision: converged adopt.go.tito.io channel stable -> edge
2026/10/22 15:22:19 provision: converged adopt.go.tito.io environment "" -> "staging"
2026/10/22 15:22:19 provision: removed retired instance route gone.go.tito.io (record gone)
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: adopted existing route story.carlosframework.com (app "story" acct "bad") — this record now owns its channel and environment
2026/10/22 15:22:19 provision: story.carlosframework.com exists but is not the route this record describes (kind "instance"/"static") — leaving it alone
2026/10/22 15:22:19 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted service route pubs.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted service route pub.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route exec.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted service route loop.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted service route named.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted service route priv.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route unit.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted static route pisite.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted static route site.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: removed retired static route site.go.tito.io (record gone)
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route acme-uk.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: converged acme.go.tito.io replicas "ie,uk" -> "uk"
2026/10/22 15:22:19 provision: converged acme.go.tito.io replicas "uk" -> ""
2026/10/22 15:22:19 provision: minted instance route acme-uk.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: converged acme-uk.go.tito.io replica mark true -> false
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route router.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 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/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route router.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route router.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route router.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:19 provision: acme.go.tito.io: not minting this pass: the box's layout intent could not be read: store down
2026/10/22 15:22:19 provision: router.go.tito.io: not minting this pass: the box's layout intent could not be read: store down
2026/10/22 15:22:19 provision: adopted existing route acme.go.tito.io (app "tito" acct "bac") — this record now owns its channel and environment
2026/10/22 15:22:19 provision: converged acme.go.tito.io channel stable -> edge
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io held landing by mv_acme (app "tito" acct "bac")
2026/10/22 15:22:19 provision: not removing acme.go.tito.io (record gone): participant of a running operation
2026/10/22 15:22:19 provision: removed retired instance route acme.go.tito.io (record gone)
2026/10/22 15:22:19 provision: minted instance route acme.go.tito.io held landing by mv_acme (app "tito" acct "bac")
2026/10/22 15:22:19 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/22 15:22:19 provision: held acme.go.tito.io landing by mv_acme (was ready)
2026/10/22 15:22:19 provision: acme.go.tito.io: held restoring by rb_other, not by mv_acme; leaving it
2026/10/22 15:22:19 provision: acme.go.tito.io: the row is not this pass's (app "other" acct ""); not holding it for mv_acme
2026/10/22 15:22:19 provision: acme.go.tito.io exists but is not the route this record describes (app "other"/"tito") — leaving it alone
2026/10/22 15:22:19 provision: dry-run: would mint instance route acme.go.tito.io held landing by mv_acme (app "tito" acct "bac" channel "stable")
2026/10/22 15:22:19 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/22 15:22:19 provision: held acme.go.tito.io landing by mv_acme (was ready)
2026/10/22 15:22:20 provision: minted instance route acme.example.com (app "shop" acct "bac")
2026/10/22 15:22:20 provision: minted instance route plain.example.com (app "shop" acct "bac")
2026/10/22 15:22:20 provision: skipping record apps/bac/shop/instances/garbled.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:20 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/22 15:22:20 provision: skipping record apps/bac/shop/instances/garbled.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:20 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/22 15:22:20 provision: skipping record apps/bac/shop/instances/garbled.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:20 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/22 15:22:20 provision: skipping record apps/bac/shop/instances/garbled.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:20 provision: listing apps/bac/shop/instances/: list refused — its records are unseen this pass
2026/10/22 15:22:20 provision: a.shop.example.com: swapped for import sd_e7qw64wythld77r3v6xiu7brde (the original route is journaled)
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_e7qw64wythld77r3v6xiu7brde committed: its gate is cleared and its journal row dropped
2026/10/22 15:22:20 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/22 15:22:20 provision: a.shop.example.com: import sd_4gq7l5ydckfi2bfllwceqzedfi rolled back to its original route
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_4gq7l5ydckfi2bfllwceqzedfi: the host's replica is left alone: the record apps/bac/shop/instances/a.shop.example.com.json holds the host
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_4gq7l5ydckfi2bfllwceqzedfi is cleaned up and its journal row retired
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_kpb4rre4otmvbnh4fchgdxw42m rolled back to its original route
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_kpb4rre4otmvbnh4fchgdxw42m: the host's replica is left alone: the record apps/bac/shop/instances/a.shop.example.com.json holds the host
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_kpb4rre4otmvbnh4fchgdxw42m is cleaned up and its journal row retired
2026/10/22 15:22:20 provision: dry-run: would redo a.shop.example.com's swap record CAS for import sd_ztbbo4rogx2mfi6fumc4beuhpe
2026/10/22 15:22:20 provision: dry-run: would clear a.shop.example.com's import gate
2026/10/22 15:22:20 provision: dry-run: would converge a.shop.example.com seed source
2026/10/22 15:22:20 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/22 15:22:20 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/22 15:22:20 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/22 15:22:20 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/22 15:22:20 provision: a.shop.example.com: import sd_ibuayngsvgue4nqyg4ngqlshou cannot roll back on this box: the activator is not wired; it stays journaled and gated
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_y636ngulqeoow3zzwi2xdescgm cannot roll back on this box: the activator is not wired; it stays journaled and gated
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_at4bqbtcz7l5s3afqumq7wbhsm is journaled, but this box cannot act on imports; its row is left alone
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_xox7r5fiygu3j33nljewnpvoyy is journaled, but this box cannot act on imports; its row is left alone
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_crphgmd4nuem36dkodiq3kk5aq is journaled, but this box cannot act on imports; its row is left alone
2026/10/22 15:22:20 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/22 15:22:20 provision: b.shop.example.com: import sd_y44ku7ltgmhkoh23g7vg7nl4t4 left alone this pass: the import journal could not be read
2026/10/22 15:22:20 provision: c.shop.example.com: import sd_n5fzxf6pfvfwzx4wesi44p4lem left alone this pass: the import journal could not be read
2026/10/22 15:22:20 provision: a.shop.example.com: import sd_imgjn7dlk534qibi4hkcly45uy committed: its gate is cleared and its journal row dropped
2026/10/22 15:22:20 provision: a.shop.example.com: not converging import sd_ti4x3ty2hv7vgbfi66tlu76o74: it is swapped but not committed, and the row on this box has no import gate
2026/10/22 15:22:20 provision: a.shop.example.com: not converging import sd_hla5vzvfhi5ypxckwd6a7tru5q: it is swapped but not committed, and the row on this box has no import gate
2026/10/22 15:22:20 provision: a.shop.example.com: not converging import sd_hjkhtbz7ldfd3sez6e77upcc6a: it is placed but not committed, and the row on this box has no import gate
2026/10/22 15:22:20 provision: a.shop.example.com: not converging import sd_7mw5isl2ghsbeniizlhhrafh2m: it is placed but not committed, and the row on this box has no import gate
2026/10/22 15:22:20 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/22 15:22:20 provision: converge seed source on a.shop.example.com: constraint failed: seed_source is read-only here (1811)
2026/10/22 15:22:20 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/22 15:22:21 provision: a.shop.example.com: import sd_h6pyxup24ai4ate7e276uez76q committed: its gate is cleared and its journal row dropped
2026/10/22 15:22:21 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/22 15:22:21 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:21 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/22 15:22:21 provision: acme.go.tito.io: not minting a seeded instance: this box does not mint nested (<nil>)
2026/10/22 15:22:21 provision: acme.go.tito.io: not minting a seeded instance: claim control is on for this box
2026/10/22 15:22:21 provision: acme.go.tito.io: aborted seed sd_h4krymm7uonymsjx2u4yto2gki: this box cannot read seed markers, and places no seed; nothing local is removed
2026/10/22 15:22:21 provision: acme.go.tito.io: aborted seed sd_h4krymm7uonymsjx2u4yto2gki rolled back (no box had swapped it)
2026/10/22 15:22:21 provision: acme.go.tito.io: not minting a seeded instance: its record does not describe a hibernating instance
2026/10/22 15:22:21 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:21 provision: removed instance route acme.go.tito.io (its seed sd_23zyupo36bc7442qq67enbwg5u was aborted); the next pass rolls it back
2026/10/22 15:22:21 provision: acme.go.tito.io: aborted seed sd_23zyupo36bc7442qq67enbwg5u: this box cannot read seed markers, and places no seed; nothing local is removed
2026/10/22 15:22:21 provision: acme.go.tito.io: aborted seed sd_23zyupo36bc7442qq67enbwg5u rolled back (no box had swapped it)
2026/10/22 15:22:21 provision: acme.go.tito.io: aborted seed sd_wxmusrqufrkeds7zfj6fn26lha has a row this pass may not remove; leaving the seed aborted
2026/10/22 15:22:21 provision: acme.go.tito.io: aborted seed sd_l3ae6yryxs5onecykmzaxwpywm rolled back (no box had swapped it)
2026/10/22 15:22:21 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:21 provision: removed instance route acme.go.tito.io (its seed sd_q3vwf3a6k47bg3bvgzvwy4ebma was aborted); the next pass rolls it back
2026/10/22 15:22:21 provision: acme.go.tito.io: aborted seed sd_q3vwf3a6k47bg3bvgzvwy4ebma has no route on this box: its local data is removed
2026/10/22 15:22:21 provision: acme.go.tito.io: aborted seed sd_q3vwf3a6k47bg3bvgzvwy4ebma rolled back (no box had swapped it)
2026/10/22 15:22:21 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_7fykrpmv7soki625nkvfz6d4ru (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_7fykrpmv7soki625nkvfz6d4ru rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_7fykrpmv7soki625nkvfz6d4ru is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_k247it6nmvf72lximpcjfkauty (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_k247it6nmvf72lximpcjfkauty rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_k247it6nmvf72lximpcjfkauty is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_yyuezvhhpge46h3mic5rxkgthe (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_yyuezvhhpge46h3mic5rxkgthe rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_yyuezvhhpge46h3mic5rxkgthe: the host's replica is left alone: its record no longer names this seed
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_yyuezvhhpge46h3mic5rxkgthe is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_sb345etplpve6walvqrbj6k5ji (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_sb345etplpve6walvqrbj6k5ji rollback deferred: the host is not fenced and stopped yet (<nil>)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_sb345etplpve6walvqrbj6k5ji rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_sb345etplpve6walvqrbj6k5ji is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_e3745pwev4eewttc7n6oetqkjy (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_e3745pwev4eewttc7n6oetqkjy rollback deferred: the host is not fenced and stopped yet (stop unproven)
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_qyf4cq77fo4yoe3lllx6punbhi (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_qyf4cq77fo4yoe3lllx6punbhi is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_nzyaa3zowh5vbsfhofqybys6h4 (the original route is journaled)
2026/10/22 15:22:21 provision: skipping record apps/bac/shop/instances/a.shop.example.com.json: malformed: provision: invalid maintenance projection or protected fields
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_nzyaa3zowh5vbsfhofqybys6h4 left alone this pass: its record could not be read
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_2yh5ldbpa35a2kimqrpvwphmxa (the original route is journaled)
2026/10/22 15:22:21 provision: skipping record apps/bac/shop/instances/a.shop.example.com.json: read failed: connection reset
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_2yh5ldbpa35a2kimqrpvwphmxa left alone this pass: its record could not be read
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_zexhviv7l77j6ud6bva4rzj7iy (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_zexhviv7l77j6ud6bva4rzj7iy: close the import gate: constraint failed: injected (1811)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_zexhviv7l77j6ud6bva4rzj7iy rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_zexhviv7l77j6ud6bva4rzj7iy is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_jr3rhhze5egl7kq6qb2fhbndsm (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_jr3rhhze5egl7kq6qb2fhbndsm: close the import gate: constraint failed: injected (1811)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_jr3rhhze5egl7kq6qb2fhbndsm rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_jr3rhhze5egl7kq6qb2fhbndsm is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_24badsmiucyumcwrualekoz7sy (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_24badsmiucyumcwrualekoz7sy rollback deferred: the host is not fenced and stopped yet (injected failure at fence)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_24badsmiucyumcwrualekoz7sy rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_24badsmiucyumcwrualekoz7sy is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_wp66k2skilex2qy3zwlmmnoqva (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_wp66k2skilex2qy3zwlmmnoqva rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_wp66k2skilex2qy3zwlmmnoqva is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_wzlrwggxhoodhi67qdio6bivxu (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_wzlrwggxhoodhi67qdio6bivxu: retire its instance key: injected failure at retire-key
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_wzlrwggxhoodhi67qdio6bivxu rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_wzlrwggxhoodhi67qdio6bivxu is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_xvohyi2gj5sua6n3rnqpazidyu (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_xvohyi2gj5sua6n3rnqpazidyu rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_xvohyi2gj5sua6n3rnqpazidyu is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_5cx67unfpwksbsdlbk6hphdeeq (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_5cx67unfpwksbsdlbk6hphdeeq: remove its local data: injected failure at remove-local
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_5cx67unfpwksbsdlbk6hphdeeq rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_5cx67unfpwksbsdlbk6hphdeeq is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_pgrfo4kusi5ztb2zlmgbhmt6a4 (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_pgrfo4kusi5ztb2zlmgbhmt6a4 rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_pgrfo4kusi5ztb2zlmgbhmt6a4 is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_vodqkgshjwxcjoheazvjx6kfom (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_vodqkgshjwxcjoheazvjx6kfom: remove its local data: injected failure at remove-local-done
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_vodqkgshjwxcjoheazvjx6kfom rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_vodqkgshjwxcjoheazvjx6kfom is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_mr4zw2zzaozk2ikfanghm3lxne (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_mr4zw2zzaozk2ikfanghm3lxne rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_mr4zw2zzaozk2ikfanghm3lxne is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_fm7cxroivvddqpdgg46f23qa64 (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_fm7cxroivvddqpdgg46f23qa64: restore the original route: constraint failed: injected (1811)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_fm7cxroivvddqpdgg46f23qa64 rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_fm7cxroivvddqpdgg46f23qa64 is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_bsanmp3myrjxyb7ymdntmzxo5u (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_bsanmp3myrjxyb7ymdntmzxo5u: restore the original route: constraint failed: injected (1811)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_bsanmp3myrjxyb7ymdntmzxo5u rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_bsanmp3myrjxyb7ymdntmzxo5u is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_fsymuvef3wh3leizxmqkre2coi (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_fsymuvef3wh3leizxmqkre2coi is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_kfq3a7uggkurgja4ifyglwybpm (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_kfq3a7uggkurgja4ifyglwybpm rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_kfq3a7uggkurgja4ifyglwybpm: delete its snapshot: injected failure at delete-snapshot
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_kfq3a7uggkurgja4ifyglwybpm is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_jsc2lv32mwvop4qejehfq2ocdm (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_jsc2lv32mwvop4qejehfq2ocdm rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_jsc2lv32mwvop4qejehfq2ocdm is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_37dypiqxicxhw7gvwpgrbyphlu (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_37dypiqxicxhw7gvwpgrbyphlu rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_37dypiqxicxhw7gvwpgrbyphlu: delete the host's replica: injected failure at delete-replica
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_37dypiqxicxhw7gvwpgrbyphlu is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_nrr63andgjrvtvsm4i3fvcelia (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_nrr63andgjrvtvsm4i3fvcelia rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_nrr63andgjrvtvsm4i3fvcelia is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_vddijs3rknqqmdap4uty4kanhe (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_vddijs3rknqqmdap4uty4kanhe rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_vddijs3rknqqmdap4uty4kanhe: retire its journal row: constraint failed: injected (1811)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_vddijs3rknqqmdap4uty4kanhe is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_732zfjxqm4yniivwnm6okfflau (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_732zfjxqm4yniivwnm6okfflau rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_732zfjxqm4yniivwnm6okfflau: retire its journal row: constraint failed: injected (1811)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_732zfjxqm4yniivwnm6okfflau is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_todlvm7mdhfsluu54exwion5pe (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_todlvm7mdhfsluu54exwion5pe rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_todlvm7mdhfsluu54exwion5pe is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: mark import sd_todlvm7mdhfsluu54exwion5pe rolled back: injected failure at cas
2026/10/22 15:22:21 provision: a.shop.example.com: aborted seed sd_todlvm7mdhfsluu54exwion5pe rolled back (no box had swapped it)
2026/10/22 15:22:21 provision: a.shop.example.com: swapped for import sd_pxrihziirms6nwf6vmq5xddunm (the original route is journaled)
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_pxrihziirms6nwf6vmq5xddunm rolled back to its original route
2026/10/22 15:22:21 provision: a.shop.example.com: import sd_pxrihziirms6nwf6vmq5xddunm is cleaned up and its journal row retired
2026/10/22 15:22:21 provision: a.shop.example.com: aborted seed sd_pxrihziirms6nwf6vmq5xddunm rolled back (no box had swapped it)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_chiaokknueepxd5fyocqix7idu (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_chiaokknueepxd5fyocqix7idu rollback deferred: the host is not fenced and stopped yet (<nil>)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_chiaokknueepxd5fyocqix7idu rollback deferred: the host is not fenced and stopped yet (<nil>)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_chiaokknueepxd5fyocqix7idu rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_chiaokknueepxd5fyocqix7idu is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_p6yn6ppphqmu5yrc2wcsur3zxi (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_p6yn6ppphqmu5yrc2wcsur3zxi rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_p6yn6ppphqmu5yrc2wcsur3zxi is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_wxugmm5pd5qyfxjuncxo2phttq (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_wxugmm5pd5qyfxjuncxo2phttq rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_wxugmm5pd5qyfxjuncxo2phttq is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_otgd23fyyapkwuwd7shhetzmju (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_otgd23fyyapkwuwd7shhetzmju rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_otgd23fyyapkwuwd7shhetzmju is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_kuk5uiyuqigkrdcgp7hl3yieo4 (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_kuk5uiyuqigkrdcgp7hl3yieo4 rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_kuk5uiyuqigkrdcgp7hl3yieo4 is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_sjmdzihotgh77muupqbfowypcq (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_sjmdzihotgh77muupqbfowypcq rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_sjmdzihotgh77muupqbfowypcq is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: minted instance route a.shop.example.com (app "other" acct "bac")
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_qegwm5uyyaxr53rajyadeajayu (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_qegwm5uyyaxr53rajyadeajayu rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_qegwm5uyyaxr53rajyadeajayu is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: minted instance route a.shop.example.com (app "other" acct "bac")
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_rnewqmdda5lrfl3ucu4tvhdu2y (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_rnewqmdda5lrfl3ucu4tvhdu2y rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_rnewqmdda5lrfl3ucu4tvhdu2y is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: minted instance route a.shop.example.com (app "other" acct "bac")
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_qvvy5qchpfwbh6fwse7yd7lcyi (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_qvvy5qchpfwbh6fwse7yd7lcyi has no route on this box: its local data is removed and no route is put back
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_qvvy5qchpfwbh6fwse7yd7lcyi is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_ezhjqrqy752aygo6adfvouju6m (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_72usxwes546njroykxy2lvnqfu (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_yhwqdct7prdplcbliyiylpngvu (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_yhwqdct7prdplcbliyiylpngvu cannot roll back on this box: the activator is not wired; it stays journaled and gated
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_cmiqzw4baqlxru3mbtswt7wfuu (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_cmiqzw4baqlxru3mbtswt7wfuu is not rolled back: the route changed under the fence, so nothing there is touched
2026/10/22 15:22:22 provision: a.shop.example.com: import "../not-a-seed" names no snapshot and no replica; only its journal row is cleaned up
2026/10/22 15:22:22 provision: a.shop.example.com: import ../not-a-seed is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_rm25rzqhfc5x6kwiof62jrflqm (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_3iex2lk6lcw3s76pgiqbkbs5vm (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_d6ksojljgjym44gpbi5rwvgwfe (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_i5a64iugswvjhsaoungcrfjtte (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_rychyrey6mbjvpnybn45ondgyu (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_rychyrey6mbjvpnybn45ondgyu rollback deferred: the host is not fenced and stopped yet (stop evidence unreadable)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_dtbdphbdwpq22tlwlwmd6r77hq (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_dtbdphbdwpq22tlwlwmd6r77hq has no route on this box: its local data is removed and no route is put back
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_dtbdphbdwpq22tlwlwmd6r77hq is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_nf74g6yychrr5avo3sa772g6pm (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_nf74g6yychrr5avo3sa772g6pm: remove its local data: injected failure at remove-local
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_nf74g6yychrr5avo3sa772g6pm is not rolled back: the route at the host is not the import's, so nothing there is touched
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_ncik4augp3eu3avo7wij54tvpq (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_ncik4augp3eu3avo7wij54tvpq: a route appeared at the host, so its local database is left and the rollback waits
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_krt7urrqsn6qyg4hofb7gp3pli (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_krt7urrqsn6qyg4hofb7gp3pli rollback deferred: no fence generation: registry busy
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_vh7s5fwrb2oenplr5ybkty36da (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_vh7s5fwrb2oenplr5ybkty36da rollback deferred: no fence generation: the generation source answered 0
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_gjjad4i4pjqvvffo6jayp4jox4 (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_ahip2iuohbi4vfdhrov33kuvii (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_4ofepqeeajbx6hm52yk6fkj4ma (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import deferred: this box cannot read the instance's env file
2026/10/22 15:22:22 provision: a.shop.example.com: import deferred: the instance's env file could not be read: a symlink
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_55kmv6himhr3djac5zjt34en3q (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_55kmv6himhr3djac5zjt34en3q rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_55kmv6himhr3djac5zjt34en3q is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_eyc4mmtfct5jhj3kcucr6zvxn4 (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_eyc4mmtfct5jhj3kcucr6zvxn4 rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_eyc4mmtfct5jhj3kcucr6zvxn4 is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_fkopge6x2u5lwn4veuvjgytduq (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_fkopge6x2u5lwn4veuvjgytduq rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_fkopge6x2u5lwn4veuvjgytduq is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_jixxukgycysm2t3euap3enbjay (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_jixxukgycysm2t3euap3enbjay: retire its instance key: the env file is a symlink
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_jixxukgycysm2t3euap3enbjay rolled back to its original route
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_jixxukgycysm2t3euap3enbjay is cleaned up and its journal row retired
2026/10/22 15:22:22 provision: a.shop.example.com: swapped for import sd_xoikmhakzhumliwjtqbi4w3e34 (the original route is journaled)
2026/10/22 15:22:22 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/22 15:22:22 provision: a.shop.example.com: swapped for import sd_sv2zpitidp5tptpqmho6rgpqeq (the original route is journaled)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_mccchod7vlbxwtyp6picovy7b4 refused: there is no route at a.shop.example.com to replace
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_hzdu7wibe53qflxodnkir3szum refused: the route's address is 127.0.0.1:9, not 127.0.0.1:8401
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_pdci7h22xu3mwhykg6aqrpopve refused: the route's address is 127.0.0.1:8401,127.0.0.1:8402, not 127.0.0.1:8401
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_ssbf37gxxl3szrhzmamy2p64lm refused: the route's address is 127.0.0.1:9, not 127.0.0.1:8401
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_zhxmozl66s7g5ialjmdatwb3iu refused: the route belongs to app other
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_5mqcbz3fvc7kgf2tibzfj6zllu refused: the route is filed under another account
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_fzb4myewnapzet5m5rhz2ecel4 refused: the route is filed under the operator account, and no assignment maps shop to this app
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_hvunrvwbe2uhkg7sds5dp2frjm refused: the route is filed under the operator account, and no assignment maps shop to this app
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_u2f3vgqc4c4rdjobbszhhmr4rq refused: the route is filed under the operator account, and no assignment maps shop to this app
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_655phxs7ocpv6z2klibnfnonjm refused: the route is filed under the operator account, and no assignment maps shop to this app
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_kovnqhllewlbnw4p3ujghcobjq refused: the route is not an address route this import can take over (socket)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_e63d4rpgqsvelp44o3rubof4nm refused: the route is not an address route this import can take over (no address)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_whs2eq2ukgbguo2ykjke7hpyyi refused: the route is not an address route this import can take over (provisioned)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_hdai5mmzi5mn32l2ixfqalcro4 refused: the route is not an address route this import can take over (kind static)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_45r4ogpvqokdl6gw5737czxj5i refused: the route is not an address route this import can take over (backing)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_v2rsqbt6yekuepnsymbgdned54 refused: the route is not an address route this import can take over (tunnel)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_b5zdw5qc4gryzvouhhvg2erkvu refused: the route is not an address route this import can take over (caps)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_fzsqepom2rndynah2gtekqgrfm refused: the route is not an address route this import can take over (platform)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_zrgm37s7huy6z2cehzvbjcoy6q refused: the route is not an address route this import can take over (canary)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_6ezkhmoalwsggfus2tnwbktsqe refused: the route is not an address route this import can take over (kind sidecar)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_amliouhzfthyrbmqzeelfax7mu refused: the route is not an address route this import can take over (region)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_ieeasrnvd5k2imj55wrxsw2moi refused: the route is not an address route this import can take over (fleet_box)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_adnrudriozujlf5bqc2azctvni refused: the route is not an address route this import can take over (hibernate)
2026/10/22 15:22:22 provision: a.shop.example.com: import sd_po2yjsvjiaioedo76t6xihh7ke refused: the route is not an address route this import can take over (claim_owner)
2026/10/22 15:22:23 provision: a.shop.example.com: import sd_cg3zwqt62srwdpuoncg7e6hruy refused: this box does not run instances nested
2026/10/22 15:22:23 provision: a.shop.example.com: import sd_7qu4lzmxv3k4fo7qwyzsbkeavy refused: this box does not run instances nested
2026/10/22 15:22:23 provision: a.shop.example.com: import sd_e6l34evta3gnnpsvgainyexjsa refused: claim control is on for this box
2026/10/22 15:22:23 provision: a.shop.example.com: swapped for import sd_ytn4vrvsvw3u66skzizafyxsje (the original route is journaled)
2026/10/22 15:22:23 provision: a.shop.example.com: swapped for import sd_b2vrjzqwybzv4ab25ricfhoa6i (the original route is journaled)
2026/10/22 15:22:23 provision: a.shop.example.com: import deferred: this box cannot read the legacy route assignments
2026/10/22 15:22:23 provision: a.shop.example.com: import deferred: the legacy route assignments could not be read: store down
2026/10/22 15:22:23 provision: a.shop.example.com: not swapping for import sd_nvhkpezzadwil2kk2jirtzmm44: its verify hash is not a sha256
2026/10/22 15:22:23 provision: a.shop.example.com: not swapping for import sd_gdbfkcdgxpz4tz7qup22q2rr5y: its verify hash is not a sha256
2026/10/22 15:22:23 provision: a.shop.example.com: not swapping for import sd_4cqdpt2bmnlyd2vj4uovz7xug4: its verify hash is not a sha256
2026/10/22 15:22:23 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/22 15:22:23 provision: A.shop.example.com/..: not swapping for import sd_u4iofaksg6ndbc6infspim5jla: its host or seed id is malformed (seeds: not a host)
2026/10/22 15:22:23 provision: a.shop.example.com: not swapping for import sd_5ilw64oaaaaoa3ydnzvilpsnze: its record does not describe a nested, hibernating instance
2026/10/22 15:22:23 provision: a.shop.example.com: not swapping for import sd_usjtmnq5laxn7f2aeh5ssxrrd4: its record does not describe a nested, hibernating instance
2026/10/22 15:22:23 provision: a.shop.example.com: not swapping for import sd_35dndoloidscgl3wsvdqplugya: its record does not describe a nested, hibernating instance
2026/10/22 15:22:23 provision: a.shop.example.com: not swapping for import sd_h2rrxyjhpzjzksqizuoc5iot74: its record does not describe a nested, hibernating instance
2026/10/22 15:22:23 provision: a.shop.example.com: import deferred: the box's layout intent could not be read: store down
2026/10/22 15:22:23 provision: a.shop.example.com: swapped for import sd_3xmxioa6pecafamvhfv374gwoi (the original route is journaled)
2026/10/22 15:22:23 provision: a.shop.example.com: import deferred: the box's nested probe did not answer: ready probe timed out
2026/10/22 15:22:23 provision: a.shop.example.com: swapped for import sd_3igl3liatg2zutjathldpyzkfm (the original route is journaled)
2026/10/22 15:22:23 provision: a.shop.example.com: swapped for import sd_xnwsqmee4hauy6ijjyavqsgivm (the original route is journaled)
2026/10/22 15:22:23 provision: a.shop.example.com: import deferred: the route is held by operation op_1
2026/10/22 15:22:23 provision: dry-run: would consider swapping a.shop.example.com for import sd_ugepfuyfvll6czp6ke4qcivgse
2026/10/22 15:22:23 provision: a.shop.example.com: import sd_n3ndnau6fmm4epg4p4hozp37gq rolled back to its original route
2026/10/22 15:22:23 provision: a.shop.example.com: import sd_n3ndnau6fmm4epg4p4hozp37gq: the host's replica is left alone: the record apps/bac/shop/instances/a.shop.example.com.json holds the host
2026/10/22 15:22:23 provision: a.shop.example.com: import sd_n3ndnau6fmm4epg4p4hozp37gq is cleaned up and its journal row retired
2026/10/22 15:22:23 provision: a.shop.example.com: import sd_vipvoier45s5fa3civpbu4r2ia left alone: the import journal holds seed sd_zk6jbx67irjtt6bt67hutj7wsm at swapped
2026/10/22 15:22:23 provision: a.shop.example.com: not minting import sd_jhkm227rm6gm7mdvgeyfo7z67y: it is swapped but not committed, and only its swap makes its row
2026/10/22 15:22:23 provision: a.shop.example.com: not minting import sd_ctooyrfnjqbfwmktessv66bgkq: it is placed but not committed, and only its swap makes its row
2026/10/22 15:22:23 provision: a.shop.example.com: swapped for import sd_nti6xqc4xfixpvqzufectnw5sy (the original route is journaled)
2026/10/22 15:22:23 provision: a.shop.example.com: not minting import sd_nti6xqc4xfixpvqzufectnw5sy: it is swapped but not committed, and only its swap makes its row
2026/10/22 15:22:23 provision: a.shop.example.com: not minting import sd_nti6xqc4xfixpvqzufectnw5sy: it is placed but not committed, and only its swap makes its row
2026/10/22 15:22:23 provision: minted instance route a.shop.example.com (app "shop" acct "bac")
2026/10/22 15:22:23 provision: removed retired sidecar route gone.eleven.bac.sidecar.internal (record gone)
2026/10/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 provision: converged sidecar bot.eleven.bac.sidecar.internal args ["sidecar","run"] -> ["worker","run"]
2026/10/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 provision: converged sidecar bot.eleven.bac.sidecar.internal ceilings 300/3600 -> 60/120
2026/10/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 provision: converged sidecar bot.eleven.bac.sidecar.internal channel stable -> canary/eleven
2026/10/22 15:22:23 provision: converged sidecar bot.eleven.bac.sidecar.internal environment "other" -> "relay"
2026/10/22 15:22:23 provision: converged sidecar bot.eleven.bac.sidecar.internal hibernate true -> false
2026/10/22 15:22:23 provision: skipping sidecar record apps/bac/eleven/sidecars/bot.json: read failed: failStore: reads disabled under apps/bac/eleven/sidecars/
2026/10/22 15:22:23 provision: skipping sidecar record apps/bac/eleven/sidecars/bot.json: malformed: invalid character 'n' looking for beginning of object key string
2026/10/22 15:22:23 provision: not removing sidecar bot.eleven.bac.sidecar.internal: its record exists but could not be read cleanly this pass
2026/10/22 15:22:23 provision: skipping sidecar record apps/bac/eleven/sidecars/bot.json: empty channel
2026/10/22 15:22:23 provision: skipping sidecar record apps/bac/eleven/sidecars/held.json: empty channel
2026/10/22 15:22:23 provision: not removing sidecar held.eleven.bac.sidecar.internal: its record exists but could not be read cleanly this pass
2026/10/22 15:22:23 provision: removed retired sidecar route old.eleven.bac.sidecar.internal (record gone)
2026/10/22 15:22:23 provision: bac/eleven has sidecar records but no sidecars manifest — not minting
2026/10/22 15:22:23 provision: bac/eleven is in the trash — not minting sidecar routes for it
2026/10/22 15:22:23 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/22 15:22:23 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/22 15:22:23 provision: minted sidecar route hub.relay.bac.sidecar.internal (app "relay" acct "bac" args ["relay"])
2026/10/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 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/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 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/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 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/22 15:22:23 provision: removed retired sidecar route bot.eleven.bac.sidecar.internal (record gone)
2026/10/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 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/22 15:22:23 provision: skipping sidecar record apps/bac/relay/sidecars/hub.json: read failed: failStore: reads disabled under apps/bac/relay/sidecars/
2026/10/22 15:22:23 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/22 15:22:23 provision: minted sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" args ["sidecar","run"])
2026/10/22 15:22:23 provision: dry-run: would mint sidecar route bot.eleven.bac.sidecar.internal (app "eleven" acct "bac" channel "stable" args ["sidecar","run"])
2026/10/22 15:22:23 provision: dry-run: would remove retired sidecar route gone.eleven.bac.sidecar.internal (record gone)
2026/10/22 15:22:23 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/22 15:22:23 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:23 provision: converged acme.go.tito.io update settings "{\"updates\":\"manual\",\"channel\":\"stable\"}" -> ""
2026/10/22 15:22:23 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:23 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:23 provision: manifest bac/tito: read failed: provision: manifest bac/tito: unexpected end of JSON input
2026/10/22 15:22:23 provision: manifest bac/tito: read failed: provision: manifest bac/tito: unexpected end of JSON input
2026/10/22 15:22:23 provision: acme.go.tito.io: not minted this pass: the app's update policy could not be read
2026/10/22 15:22:23 provision: minted instance route acme.go.tito.io (app "tito" acct "bac")
2026/10/22 15:22:23 provision: manifest bac/tito: read failed: provision: manifest bac/tito: unexpected end of JSON input
2026/10/22 15:22:23 provision: acme.go.tito.io: not minted this pass: the app's update policy could not be read
2026/10/22 15:22:23 provision: minted instance route acme.go.tito.io held landing by mv_acme (app "tito" acct "bac")
--- FAIL: TestPlan14RootReadCensus (39.59s)
    plan14_root_reads_test.go:1329: root read census: 9 discrepancies
        changed root read internal/edge/static.go:PackDir; review class, task, sites and digest
        changed root source envelope internal/edge
        stale root read internal/edge/static.go:staticSite.serveTree
        unclassified root read internal/edge/static.go:lookupTree file:Stat@188,file:Stat@282
        unclassified root read internal/edge/static.go:staticSite.loadRedirects file:ReadFile@87
        unclassified root read internal/edge/static.go:staticSite.serveFile file:Open@114,file:Stat@859
        unclassified root read internal/edge/static.go:staticSite.serveRule file:Open@509
        changed legacy declaration internal/edge/static.go:PackDir
        changed legacy declaration internal/edge/static.go:staticSite.serveTree
FAIL
FAIL	github.com/carlosframework/platform/internal/provision	49.342s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.009s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	5.783s
ok  	github.com/carlosframework/platform/internal/relay	7.206s
ok  	github.com/carlosframework/platform/internal/release	31.117s
ok  	github.com/carlosframework/platform/internal/rollout	4.143s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.003s
ok  	github.com/carlosframework/platform/internal/rootaws	0.008s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.063s
ok  	github.com/carlosframework/platform/internal/rootstate	0.027s
ok  	github.com/carlosframework/platform/internal/runs	0.618s
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.842s
ok  	github.com/carlosframework/platform/internal/seeds	0.012s
ok  	github.com/carlosframework/platform/internal/sim/engines	3.159s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	11.575s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	4.338s
ok  	github.com/carlosframework/platform/internal/status	0.074s
ok  	github.com/carlosframework/platform/internal/steering	0.007s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.272s
ok  	github.com/carlosframework/platform/internal/svcrole	0.002s
ok  	github.com/carlosframework/platform/internal/telemetry	0.004s
ok  	github.com/carlosframework/platform/internal/termfence	0.004s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.005s
ok  	github.com/carlosframework/platform/internal/unixdial	0.003s
ok  	github.com/carlosframework/platform/internal/updatewords	0.004s
ok  	github.com/carlosframework/platform/internal/usage	0.005s
ok  	github.com/carlosframework/platform/internal/version	0.004s
ok  	github.com/carlosframework/platform/internal/vet	1.752s
FAIL
=== internal_console@0 (86 s)
2026/10/22 15:22:06 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:06 console: could not notify alice@keymail.dev of a CLI token issue: keymail: grant revoked or invalid
2026/10/22 15:22:06 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:06 console: could not notify alice@keymail.dev of a CLI token issue: keymail: grant revoked or invalid
2026/10/22 15:22:06 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:06 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:06 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:06 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:06 console: api instance move bab/titogo/acme.go.tito.io: the claim is moving under mv_qpnhfxv2wxn7r47qj4sal5qf4e but the record start failed: secret-detail: record put failed
2026/10/22 15:22:06 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:06 console: api instances delete bab/titogo/acme.go.tito.io: provision: claim retirement unavailable
2026/10/22 15:22:06 console: api instances delete bab/titogo/acme.go.tito.io: provision: claim retirement unavailable
2026/10/22 15:22:06 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: api instance move bab/titogo/acme.go.tito.io: typed move write refused: hostops: identity mismatch
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: api instance move bab/titogo/acme.go.tito.io: typed move write refused: hostops: identity mismatch
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: api instance move bab/titogo/acme.go.tito.io: typed move write refused: hostops: identity mismatch
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: offline move acme.shop.example.com: ignoring control/offline-moves/acme.shop.example.com/mv_zntjsmxaylpuqxponkajlaxi3u/seal.json: not the seal of this move
2026/10/22 15:22:07 console: offline move acme.shop.example.com: ignoring control/offline-moves/acme.shop.example.com/mv_zntjsmxaylpuqxponkajlaxi3u/seal.json: not the seal of this move
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: offline move bab/shop/acme.shop.example.com: write timeout
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:07 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:08 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:08 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:08 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:08 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:08 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:08 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:08 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:08 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:08 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: could not notify alice@keymail.dev of a CLI token issue: keymail: grant revoked or invalid
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: could not notify alice@keymail.dev of a CLI token issue: keymail: grant revoked or invalid
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:09 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:09 console: app region bab/rot-a0:  (default)
2026/10/22 15:22:09 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:09 console: app region bab/rot-a1:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a2:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a3:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a4:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a5:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a6:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a7:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a8:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a9:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a10:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a11:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a12:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a13:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a14:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a15:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a16:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a17:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a18:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a19:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a20:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a21:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a22:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a23:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a24:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a25:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a26:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a27:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a28:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a29:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a30:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a31:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a32:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a33:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a34:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a35:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a36:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a37:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a38:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a39:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a40:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a41:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a42:  (default)
2026/10/22 15:22:09 console: app region bab/rot-a43:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a44:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a45:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a46:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a47:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a48:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a49:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a50:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a51:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a52:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a53:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a54:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a55:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a56:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a57:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a58:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a59:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a60:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a61:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a62:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a63:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a64:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a65:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a66:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a67:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a68:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a69:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a70:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a71:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a72:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a73:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a74:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a75:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a76:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a77:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a78:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a79:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a80:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a81:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a82:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a83:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a84:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a85:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a86:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a87:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a88:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a89:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a90:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a91:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a92:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a93:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a94:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a95:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a96:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a97:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a98:  (default)
2026/10/22 15:22:10 console: app region bab/rot-a99:  (default)
2026/10/22 15:22:10 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:10 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:10 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:10 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:10 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:22:10 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:10 console: api relay enable: hostops: pending
2026/10/22 15:22:11 console: relay enabled bab/titogo in eu-west-1 by paul@keymail.dev (incarnation 43a1cdfb5dcc010c, op 1)
2026/10/22 15:22:11 console: relay enabled bab/titogo in eu-west-1 by paul@keymail.dev (incarnation ebc28db69fb24c96, op 1)
2026/10/22 15:22:11 console: api relay adopted: hostops: pending
2026/10/22 15:22:12 console: relay enabled bab/titogo in eu-west-1 by paul@keymail.dev (incarnation 48018c7fa65882d5, op 1)
2026/10/22 15:22:12 console: api relay disable: hostops: pending
2026/10/22 15:22:12 console: relay disabled bab/titogo by paul@keymail.dev (op 2)
2026/10/22 15:22:13 console: relay enabled bab/titogo in eu-west-1 by paul@keymail.dev (incarnation 811baeef3bc9ff6c, op 1)
2026/10/22 15:22:13 console: relay rotate eu-west-1: bab/titogo: hostops: pending
2026/10/22 15:22:13 console: relay rotated eu-west-1 to kid k2, re-issued 0 app(s)
2026/10/22 15:22:13 console: audit relay-rotate platform: sql: no rows in result set
2026/10/22 15:22:13 console: relay rotated eu-west-1 to kid k3, re-issued 1 app(s)
2026/10/22 15:22:13 console: audit relay-rotate platform: sql: no rows in result set
2026/10/22 15:22:14 console: mail push: bab/titogo: hostops: pending
2026/10/22 15:22:21 console: relay enabled bab/titogo in eu-west-1 by paul@keymail.dev (incarnation 42558575cf5be7a7, op 1)
2026/10/22 15:22:21 console: relay reconcile bab/titogo: hostops: pending
2026/10/22 15:22:21 console: relay writer lease acquired (holder f3dac6a3b4207377)
2026/10/22 15:22:24 console: relay writer lease acquired (holder f3dac6a3b4207377)
2026/10/22 15:22:28 console: app region bab/tmp-claim-expiry:  (default)
2026/10/22 15:22:28 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:28 console: scan drain: bab/titogo: hostops: pending
2026/10/22 15:22:28 console: expiry pass: trash bab/tmp-claim-expiry: hostops: pending
2026/10/22 15:22:28 console: scan pass: bab/titogo: hostops: pending
2026/10/22 15:22:28 console: door keys re-sign bab/titogo: hostops: pending
2026/10/22 15:22:28 console: api scan request bab/titogo: hostops: pending
2026/10/22 15:22:30 console: api telemetry bab/titogo: hostops: pending
2026/10/22 15:22:30 console: api version-target set bab/titogo: hostops: pending
2026/10/22 15:22:30 console: api updates policy bab/titogo: hostops: pending
2026/10/22 15:22:31 console: api sidecars enable bab/titogo: hostops: pending
2026/10/22 15:22:31 console: api swap bab/titogo: hostops: pending
2026/10/22 15:22:31 console: api pipeline put bab/titogo: set: hostops: pending
2026/10/22 15:22:32 console: api updates bab/titogo/acme.go.tito.io: hostops: pending
2026/10/22 15:22:32 console: api instances enable bab/titogo: hostops: pending
2026/10/22 15:22:33 console: api email push key bab/titogo: hostops: pending
2026/10/22 15:22:33 console: api config apply bab/titogo (default): hostops: pending
2026/10/22 15:22:33 console: claim restore bab/titogo: recovered (restored)
2026/10/22 15:22:33 console: api domains paths bab/titogo ti.to: hostops: pending
2026/10/22 15:22:34 console: control-plane clear bab/titogo: hostops: pending
2026/10/22 15:22:34 console: cli token revoke paul@keymail.dev: hostops: pending
2026/10/22 15:22:34 console: control-plane clear bab/titogo: hostops: pending
2026/10/22 15:22:35 console: ses events: logging Delivery for bab/titogo: hostops: pending
2026/10/22 15:22:35 console: ses events: recording Delivery for bab/titogo: hostops: pending
2026/10/22 15:22:35 console: service rotate bab/svc-a: hostops: pending
2026/10/22 15:22:36 console: service revoke bab: hostops: pending
2026/10/22 15:22:36 console: service create bab/svc-cli: hostops: pending
2026/10/22 15:22:36 console: app region bab/tmp-claim-ttl:  (default)
2026/10/22 15:22:36 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:36 console: lifecycle tmp-claim-ttl: hostops: pending
2026/10/22 15:22:36 console: api scan revoke bab/titogo: hostops: pending
2026/10/22 15:22:36 console: api scan sweep bab/titogo: hostops: pending
2026/10/22 15:22:37 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:22:37 console: api scan enqueue bab/titogo "claim-fixture.txt": hostops: pending
2026/10/22 15:22:37 console: api scan grant bab/titogo: enable: hostops: pending
2026/10/22 15:22:37 console: api scan callback rotate bab/titogo: deliver: hostops: pending
2026/10/22 15:22:37 console: api scan enforce bab/titogo: hostops: pending
2026/10/22 15:22:38 console: api instance channel bab/titogo/web.go.tito.io: hostops: pending
2026/10/22 15:22:38 console: api instance token bab/titogo: hostops: pending
2026/10/22 15:22:38 console: api features set bab/titogo: hostops: pending
2026/10/22 15:22:38 console: claim request refused: hostops: pending
2026/10/22 15:22:38 console: api email rotate bab/titogo: mail: lifecycle resource operation pending
hostops: pending
2026/10/22 15:22:39 console: api email enable bab/titogo: apply grant: mail: lifecycle resource operation pending
hostops: pending
2026/10/22 15:22:39 console: api email pause bab/titogo: mail: lifecycle resource operation pending
hostops: pending
2026/10/22 15:22:39 console: api email domain verify bab/titogo.bab.oncarlos.com: mail: lifecycle resource operation pending
hostops: pending
2026/10/22 15:22:39 console: api email domain ensure bab/titogo (titogo.bab.oncarlos.com): mail: lifecycle resource operation pending
hostops: pending
2026/10/22 15:22:39 console: door key revoke bab/titogo dcc4ca1b416d6b4c: hostops: pending
2026/10/22 15:22:39 console: api email declare bab/titogo: mail: lifecycle resource operation pending
hostops: pending
2026/10/22 15:22:39 console: api email credentials create bab/titogo: mail: lifecycle resource operation pending
hostops: pending
2026/10/22 15:22:39 console: door key mint bab/titogo: hostops: pending
2026/10/22 15:22:40 console: api trash bab/titogo: hostops: pending
2026/10/22 15:22:40 console: api config sync bab/titogo: hostops: pending
2026/10/22 15:22:40 console: api resign bab/kass canary/x: hostops: pending
2026/10/22 15:22:40 console: app region bab/tmp-claim-cli:  (default)
2026/10/22 15:22:40 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:40 console: app region bab/claim-cli-new:  (default)
2026/10/22 15:22:40 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:40 console: restore bab/titogo: hostops: pending
2026/10/22 15:22:41 console: claim request refused: hostops: pending
2026/10/22 15:22:41 console: service revoke bab: hostops: pending
2026/10/22 15:22:41 console: service rotate bab/svc-a: hostops: pending
2026/10/22 15:22:41 console: service create bab/svc-web: hostops: pending
2026/10/22 15:22:41 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:22:41 console: swap policy bab/kass: hostops: pending
2026/10/22 15:22:41 console: store destroy bab/titogo: hostops: pending
2026/10/22 15:22:41 console: rollback kass canary/x: hostops: pending
2026/10/22 15:22:42 console: promote kass v1 -> canary/x: hostops: pending
2026/10/22 15:22:42 console: browser restart bab/kass/: hostops: pending
2026/10/22 15:22:42 console: production kass -> true: hostops: pending
2026/10/22 15:22:42 console: canary kass v1 -> canary/rc: hostops: pending
2026/10/22 15:22:42 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:42 console: reject pending change 6eb8bdb449ba4c28d7bb89f696f5c588: hostops: pending
2026/10/22 15:22:42 console: pipeline settings save bab/titogo: set: hostops: pending
2026/10/22 15:22:42 console: confirm pending change: hostops: pending
2026/10/22 15:22:43 console: app region bab/tmp-claim-keep:  (default)
2026/10/22 15:22:43 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:43 console: keep bab/tmp-claim-keep: hostops: pending
2026/10/22 15:22:43 console: api updates install bab/titogo/acme.go.tito.io: hostops: pending
2026/10/22 15:22:43 console: api updates bab/titogo/acme.go.tito.io: hostops: pending
2026/10/22 15:22:43 console: browser restart bab/kass/kass.carlosapp.dev: hostops: pending
2026/10/22 15:22:43 console: browser retry bab/titogo/web.go.tito.io: hostops: pending
2026/10/22 15:22:43 console: fast-track kass stable: hostops: pending
2026/10/22 15:22:43 console: gate write bab/titogo/acme.go.tito.io: hostops: pending
2026/10/22 15:22:44 console: errors level bab/kass: hostops: pending
2026/10/22 15:22:44 console: trash bab/titogo: hostops: pending
2026/10/22 15:22:44 console: control-plane set bab/titogo: hostops: pending
2026/10/22 15:22:44 console: control-plane clear bab/titogo: hostops: pending
2026/10/22 15:22:44 console: retire kass canary/rc: hostops: pending
2026/10/22 15:22:44 console: api promote kass v1 -> canary/claim: hostops: pending
2026/10/22 15:22:45 console: api store rotate finish bab/titogo: hostops: pending
2026/10/22 15:22:45 console: api store rotate bab/titogo: hostops: pending
2026/10/22 15:22:45 console: api store provision bab/titogo: hostops: pending
2026/10/22 15:22:45 console: api store grant bab/titogo (bucket ""): hostops: pending
2026/10/22 15:22:45 console: api store create bab/titogo: hostops: pending
2026/10/22 15:22:45 console: claim request refused: hostops: pending
2026/10/22 15:22:45 console: api sidecars wake bab/titogo/bot: hostops: pending
2026/10/22 15:22:45 console: api schedule run bab/titogo sync: hostops: pending
2026/10/22 15:22:46 console: api schedule rm bab/titogo sync: hostops: pending
2026/10/22 15:22:46 console: api schedule set bab/titogo sync: hostops: pending
2026/10/22 15:22:46 console: api rollback kass canary/x: hostops: pending
2026/10/22 15:22:46 console: api retention set bab/titogo: hostops: pending
2026/10/22 15:22:46 console: api restore bab/titogo: hostops: pending
2026/10/22 15:22:46 console: api restart scope bab/kass (host kass.carlosapp.dev): hostops: pending
2026/10/22 15:22:46 console: api restart bab/titogo: hostops: pending
2026/10/22 15:22:46 console: api authorize bab/kass v1: hostops: pending
2026/10/22 15:22:46 console: api ship titogo abc1234: hostops: pending
2026/10/22 15:22:47 console: analytics setting bab/kass: hostops: pending
2026/10/22 15:22:47 console: api instances update bab/titogo/ha.go.tito.io: hostops: pending
2026/10/22 15:22:47 console: app region bab/claim-web-new:  (default)
2026/10/22 15:22:47 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:47 console: control-plane clear bab/titogo: hostops: pending
2026/10/22 15:22:47 console: audit db-read titogo: hostops: pending
2026/10/22 15:22:48 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:22:48 console: app region bab/tmp-claim-keep:  (default)
2026/10/22 15:22:48 console: backfilled account manifest for bab (ops)
2026/10/22 15:22:48 console: ledger blob bab/titogo/prompts: hostops: pending
2026/10/22 15:22:48 console: audit sidecar-delete titogo: hostops: pending
2026/10/22 15:22:48 console: ledger append bab/titogo/carbon: hostops: pending
2026/10/22 15:22:48 console: ledger publish bab/titogo: hostops: pending
2026/10/22 15:22:48 console: lifecycle tmp-claim-keep: hostops: pending
2026/10/22 15:22:48 console: api updates install bab/titogo/acme.go.tito.io: hostops: pending
2026/10/22 15:22:49 console: api email push off bab/titogo: hostops: pending
2026/10/22 15:22:49 console: api email domain remove bab/titogo (titogo.bab.oncarlos.com): mail: lifecycle resource operation pending
hostops: pending
2026/10/22 15:22:49 console: audit instance-delete titogo: hostops: pending
2026/10/22 15:22:49 console: api email credentials revoke bab/titogo (laptop): mail: lifecycle resource operation pending
hostops: pending
2026/10/22 15:22:50 console: api instance retry bab/titogo/web.go.tito.io: hostops: pending
2026/10/22 15:22:59 console: geoip published ed972738 (GeoIP2-City, manual)
2026/10/22 15:23:00 console: audit geoip off platform: sql: no rows in result set
2026/10/22 15:23:00 console: relay enabled bab/titogo in eu-west-1 by paul@keymail.dev (incarnation d091529b3e5a61db, op 1)
2026/10/22 15:23:01 console: geoip published ed972738 (GeoIP2-City, manual)
2026/10/22 15:23:02 console: geoip published ed972738 (GeoIP2-City, manual)
2026/10/22 15:23:02 console: audit geoip publish platform: sql: no rows in result set
2026/10/22 15:23:09 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:09 console: audit system-update platform: sql: no rows in result set
2026/10/22 15:23:09 console: audit fleet-add-box pi: sql: no rows in result set
2026/10/22 15:23:09 console: audit system-update platform: sql: no rows in result set
2026/10/22 15:23:09 console: audit fleet-rotate-box pi: sql: no rows in result set
2026/10/22 15:23:09 console: audit system-activation-cancel platform: sql: no rows in result set
2026/10/22 15:23:12 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:12 console: audit fleet-add-box pi: sql: no rows in result set
2026/10/22 15:23:12 console: audit store-apply : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:12 console: audit fleet-rotate-box pi: sql: no rows in result set
2026/10/22 15:23:12 console: audit store-decline : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:12 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:12 console: audit fleet-add-box pi: sql: no rows in result set
2026/10/22 15:23:12 console: audit fleet-detach-box pi: sql: no rows in result set
2026/10/22 15:23:13 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:13 console: audit fleet-add-box pi: sql: no rows in result set
2026/10/22 15:23:13 console: audit errors-level account: sql: no rows in result set
2026/10/22 15:23:13 console: audit fleet-tag-box pi: sql: no rows in result set
2026/10/22 15:23:14 console: audit system-update platform: sql: no rows in result set
2026/10/22 15:23:14 console: audit store-apply : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:14 console: audit store-apply : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:15 console: audit analytics-setting account: sql: no rows in result set
2026/10/22 15:23:15 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:15 console: audit store-apply : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:16 console: audit system-update platform: sql: no rows in result set
2026/10/22 15:23:17 console: audit pointer-digests-on platform: sql: no rows in result set
2026/10/22 15:23:17 console: audit system-update platform: sql: no rows in result set
2026/10/22 15:23:17 console: audit system-update platform: sql: no rows in result set
2026/10/22 15:23:17 console: audit seeds-floor-publish platform: sql: no rows in result set
2026/10/22 15:23:17 console: audit system-rollback platform: sql: no rows in result set
2026/10/22 15:23:18 console: object scanning enabled deployment-wide by paul@keymail.dev
2026/10/22 15:23:18 console: audit system-layout platform: sql: no rows in result set
2026/10/22 15:23:18 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:18 console: audit fleet-add-box pi: sql: no rows in result set
2026/10/22 15:23:19 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:19 console: audit fleet-add-box pi: sql: no rows in result set
2026/10/22 15:23:19 console: audit fleet-detach-box pi: sql: no rows in result set
2026/10/22 15:23:20 console: audit store-apply : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:20 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:20 console: audit fleet-add-box pi: sql: no rows in result set
2026/10/22 15:23:20 console: audit store-apply : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:20 console: audit store-approve : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:20 console: audit fleet-tag-box pi: sql: no rows in result set
2026/10/22 15:23:20 console: audit store-decline : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:20 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:20 console: audit store-apply : constraint failed: claim: app write not permitted (1811)
2026/10/22 15:23:21 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:21 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:21 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:21 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:22 console: account telemetry bab: dirstore: invalid key "apps/bab//telemetry.json"
2026/10/22 15:23:22 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:22 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:22 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:22 console: audit fleet-create pi: sql: no rows in result set
2026/10/22 15:23:22 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:22 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:23 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:23 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:23 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:23 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:23 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:24 console: backfilled account manifest for bab (ops)
--- FAIL: TestClaimConsoleMutationInvariant (74.30s)
    --- FAIL: TestClaimConsoleMutationInvariant/probes (9.16s)
        --- FAIL: TestClaimConsoleMutationInvariant/probes/route_GET_/api/cli/apps/{account}/{app}/store/scan/object (3.25s)
            claim_invariant_probes_test.go:438: the answer is missing "\"state\":\"clean\"": 200 loc="" flash=:"" body="{\"recorded\":false,\"state\":\"pending\"}\n"
2026/10/22 15:23:24 console: materialized account bac (Account) from the bucket
2026/10/22 15:23:24 console: materialized account bac (Account) from the bucket
2026/10/22 15:23:24 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:24 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:24 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:24 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:24 console: could not notify alice@keymail.dev of a CLI token issue: keymail: grant revoked or invalid
2026/10/22 15:23:24 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:24 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:24 console: fold storage/census: storage day 2026-08-10: key "my-app/releases" has an invalid account segment "my-app"; attributing to bab
2026/10/22 15:23:24 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:24 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:24 console: geoip published ed972738 (GeoIP2-City, manual)
2026/10/22 15:23:24 console: geoip published ed972738 (GeoIP2-City, manual)
2026/10/22 15:23:25 console: geoip published b3760190 (GeoIP2-Country, manual)
2026/10/22 15:23:25 console: geoip published ed972738 (GeoIP2-City, manual)
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: could not notify alice@keymail.dev of a CLI token issue: Post "http://127.0.0.1:46233/api/app/send": dial tcp 127.0.0.1:46233: connect: connection refused
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:25 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: moves: warning: kass.carlosapp.dev: the log of move mv_ou3kw3d46vmd52dvynexsl7puq at elsewhere is held: the writer refuses it (hostops: identity mismatch); its entry is kept until its incarnation is purged
2026/10/22 15:23:26 console: moves: warning: acme.go.tito.io: the log of move mv_ou3kw3d46vmd52dvynexsl7puq at stale is held: the writer refuses it (hostops: identity mismatch); its entry is kept until its incarnation is purged
2026/10/22 15:23:26 console: moves: warning: acme.go.tito.io: the log of move mv_forged at forged is held: the writer refuses it (hostops: identity mismatch); its entry is kept until its incarnation is purged
2026/10/22 15:23:26 console: moves: warning: acme.go.tito.io: the log of move mv_ou3kw3d46vmd52dvynexsl7puq at retired is held: the writer refuses it (hostops: identity mismatch); its entry is kept until its incarnation is purged
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:26 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:27 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:27 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:27 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:27 console: claim SQL receipt database moved from instance i-original to i-replacement; it has a new database ID, and effects bound to the old one stay unsettled
2026/10/22 15:23:27 console: claim SQL receipt database moved from instance i-replacement to i-original; it has a new database ID, and effects bound to the old one stay unsettled
2026/10/22 15:23:27 console: alert watch: no keymail grant for ops-b@keymail.dev — not notified (they must sign in to the console once)
2026/10/22 15:23:27 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:27 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:27 console: plans marker: write: bucket unavailable
2026/10/22 15:23:27 console: plans marker: write: bucket unavailable
2026/10/22 15:23:27 console: plans marker: write: bucket unavailable
2026/10/22 15:23:27 console: plans marker: write: bucket unavailable
2026/10/22 15:23:27 console: plans marker: write: bucket unavailable
2026/10/22 15:23:27 console: plans marker: write: bucket unavailable
2026/10/22 15:23:27 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:27 console: app region bab/homeauto: auto (chosen)
2026/10/22 15:23:27 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:27 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: relay enabled bab/titogo in eu-west-1 by alice@keymail.dev (incarnation d3a6ca50cf40c0b4, op 1)
2026/10/22 15:23:28 console: relay enabled bab/meet in eu-west-1 by alice@keymail.dev (incarnation f629aa43d97f2447, op 1)
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: bab/kass is in the trash in the bucket; marking it trashed here
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: scan pass: bab/aaa-broken: endpoint down
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: seed store check bab/shop/old: who names the environment: 1 records of bab/shop could not be read, so one may name environment "old"
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 edges: 1 edges file(s) under control/edges/ but no fresh box snapshot matched any of them, so automatic region placement sees the home alone (are edges and status in different buckets? see CARLOS_STATUS_BUCKET/CARLOS_STATUS_PREFIX)
2026/10/22 15:23:28 console: seeds readiness: box "kass-1" in region "the home region" is unconfirmed: stale heartbeat, generated 2026-10-22T14:23:28Z
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: seeds pass: list records: provision: list accounts: the bucket is unreachable (nothing swept this pass)
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: seeds pass: list snapshots: the bucket is unreachable (nothing swept this pass)
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: ses events: refusing SubscribeURL: sns: SigningCertURL is not https
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: bab/tmp-a is in the trash in the bucket; marking it trashed here
2026/10/22 15:23:28 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:28 console: bab/kass is in the trash in the bucket; marking it trashed here
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: bab/tmp-a: a restore did not remove the trash marker; it stays in the trash
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: bab/tmp-a has no trash marker and still has objects; restoring it here
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: bab/tmp-a has no trash marker and still has objects; restoring it here
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: bab/tmp-a is gone from the bucket; releasing the name here
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: bab/tmp-a has no trash marker and no objects, but the apps/ listing holds nothing else either; leaving the row. Is CARLOS_DEPLOYMENT_BUCKET pointing at the right bucket?
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: account telemetry bab: dirstore: invalid key "apps/bab//telemetry.json"
2026/10/22 15:23:29 console: account telemetry bab: dirstore: invalid key "apps/bab//telemetry.json"
2026/10/22 15:23:29 console: account telemetry bab: dirstore: invalid key "apps/bab//telemetry.json"
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: render account_settings: write tcp 127.0.0.1:33205->127.0.0.1:49398: write: broken pipe
2026/10/22 15:23:29 console: account telemetry bab: dirstore: invalid key "apps/bab//telemetry.json"
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: could not notify paul@keymail.dev of a CLI token issue: Post "http://127.0.0.1:36053/api/app/send": dial tcp 127.0.0.1:36053: connect: connection refused
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:29 console: backfilled account manifest for bab (ops)
2026/10/22 15:23:30 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/22 15:23:30 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
FAIL
FAIL	github.com/carlosframework/platform/internal/console	84.069s
FAIL
=== internal_console@1 (41 s)
ok  	github.com/carlosframework/platform/internal/console	39.523s
=== internal_console@2 (48 s)
ok  	github.com/carlosframework/platform/internal/console	45.896s
=== internal_console@3 (40 s)
ok  	github.com/carlosframework/platform/internal/console	38.690s
=== internal_console@4 (45 s)
ok  	github.com/carlosframework/platform/internal/console	43.557s
=== internal_console@5 (43 s)
ok  	github.com/carlosframework/platform/internal/console	41.245s
=== internal_console@6 (48 s)
ok  	github.com/carlosframework/platform/internal/console	46.194s
=== internal_console@7 (48 s)
ok  	github.com/carlosframework/platform/internal/console	46.449s
=== root@0 (37 s)
ok  	github.com/carlosframework/platform	35.952s
=== root@1 (46 s)
ok  	github.com/carlosframework/platform	44.735s
=== root@2 (45 s)
ok  	github.com/carlosframework/platform	43.166s

FAILED: rest internal_console@0
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/docs/site/cli.md b/docs/site/cli.md
index a964ca5ab..7b80cbc29 100644
--- a/docs/site/cli.md
+++ b/docs/site/cli.md
@@ -330,6 +330,44 @@ app is served only at its own platform address, so `--host` and
carlos deploy --temporary --ttl 3d ./dist
```
+#### Redirects for a static site
+
+Put a file named `_redirects` at the top of the site directory. It uses the
+format Netlify and Cloudflare Pages use: one rule per line, with a source, a
+destination and an optional status.
+
+```text
+# source destination status
+/old-page /new-page 301
+/blog/* /posts/:splat 301
+/docs/:slug /guide/:slug 302
+/app/* /app/index.html 200
+https://www.example.com/* https://example.com/:splat 301!
+/* /404.html 404
+```
+
+- A source is a path, or a URL with a host. A URL source applies only to
+ requests for that host, so the last-but-one rule sends `www` to the apex.
+- `:name` matches one path segment. A `*` at the end matches the rest of the
+ path, and the destination gets it as `:splat`.
+- The status is 301 when you leave it out. 302, 303, 307 and 308 are also
+ redirects. 200 serves the destination's file at the requested URL, which
+ is how a single-page app serves its client-side routes. 404 serves the
+ destination's file as the not-found page.
+- A 200 or 404 rule takes a path in the same site. A redirect can also go to
+ another host.
+- A request's query string is kept unless the destination has one of its
+ own.
+- When a file exists at the requested path, the file wins. Add `!` to the
+ status (`301!`) to make the rule win.
+- Rules apply in order, and the first rule that matches is used. A line
+ starting with `#` is a comment.
+
+`carlos ship` and `carlos deploy` refuse a `_redirects` file with a line they
+cannot use, and name the line. Netlify's conditions, such as `Country=`, and
+proxying to another host are not supported. A file can have up to 2,000
+rules. The edge does not serve the `_redirects` file itself.
+
#### Two builds at once
When a new build reaches an awake instance, it starts beside the old one,
diff --git a/internal/edge/redirects.go b/internal/edge/redirects.go
new file mode 100644
index 000000000..f38a1d416
--- /dev/null
+++ b/internal/edge/redirects.go
@@ -0,0 +1,418 @@
+// Redirect and rewrite rules for static sites: a `_redirects` file at the root
+// of a shipped tree, in the format Netlify introduced and Cloudflare Pages
+// adopted. One rule per line, "source destination [status]":
+//
+// /old /new 301
+// /blog/* /posts/:splat
+// /docs/:slug /guide/:slug 302
+// /app/* /app/index.html 200
+// https://www.example.com/* https://example.com/:splat 301!
+//
+// The subset served here:
+//
+// - A source is a path, or an http(s) URL whose host must match the
+// request's (the scheme is not compared: the edge serves https only).
+// A segment ":name" captures one path segment; a final "*" captures the
+// rest of the path, "" included, so "/blog/*" also matches "/blog"; a
+// non-empty splat keeps the request's trailing slash.
+// Matching is case-sensitive and ignores a trailing slash.
+// - A destination is a path, or for a redirect an http(s) URL. ":name" and
+// ":splat" are replaced with what the source captured; placeholders in a
+// URL's scheme or host are not. A request's query string is carried over
+// when the destination names none of its own.
+// - The status is 301 (the default), 302, 303, 307 or 308 for a redirect;
+// 200 serves the destination's file at the request's URL (a rewrite) and
+// 404 serves it with a 404 (a custom not-found page). A rewrite's
+// destination is always a file in the same tree, never another host.
+// - A file at the request path wins over every rule (Netlify's
+// "shadowing"), unless the status carries a trailing "!".
+// - The first matching rule applies. A line whose first field starts with
+// "#" is a comment, and so is everything from a field starting with "#".
+//
+// Netlify's conditions (Country=, Language=, Role=...), proxying to another
+// host and the netlify.toml form are not supported. `carlos ship` refuses a
+// tree whose _redirects has a line it cannot use (PackDir), so a site never
+// ships rules the edge would ignore; the edge itself skips such a line and
+// logs it, which only a tree packed by something else can reach.
+package edge
+
+import (
+ "errors"
+ "fmt"
+ "net/url"
+ "path"
+ "strconv"
+ "strings"
+)
+
+// redirectsFile is the rules file's name at the root of a static tree. The
+// file is configuration, not content: the edge never serves it.
+const redirectsFile = "_redirects"
+
+// Limits on one _redirects file. Matching is a scan of every rule on each
+// request the tree cannot answer, so the count is bounded the way Cloudflare
+// Pages bounds it.
+const (
+ redirectsMaxBytes = 256 << 10
+ redirectsMaxRules = 2000
+ redirectsMaxLine = 2048
+)
+
+// redirectRule is one parsed line of a _redirects file.
+type redirectRule struct {
+ line int // 1-based line number, for logs
+ host string // lowercased; "" matches any host
+ segs []string // source path segments; ":name" captures one
+ splat bool // the source ends in "*"
+ prefix string // a URL destination's "scheme://host", never substituted
+ to string // the destination template after prefix
+ status int
+ force bool // "!": applies even when a file exists at the request path
+}
+
+// redirectRules is a parsed _redirects file, immutable once built.
+type redirectRules struct {
+ rules []redirectRule
+}
+
+// isRedirect reports whether status is one a redirect rule may answer with.
+func isRedirect(status int) bool {
+ switch status {
+ case 301, 302, 303, 307, 308:
+ return true
+ }
+ return false
+}
+
+// parseRedirects parses a _redirects file. It returns the rules it could use
+// and an error naming every line it could not; callers decide which matters.
+// Ship refuses on any error, and the edge serves the usable rules.
+func parseRedirects(data []byte) (*redirectRules, error) {
+ if len(data) > redirectsMaxBytes {
+ return nil, fmt.Errorf("%s is %d bytes; the limit is %d", redirectsFile, len(data), redirectsMaxBytes)
+ }
+ rs := &redirectRules{}
+ var errs []error
+ // A byte-order mark is what some editors save at the front; it is not
+ // part of the first rule.
+ text := strings.TrimPrefix(string(data), "\ufeff")
+ for i, line := range strings.Split(text, "\n") {
+ n := i + 1
+ line = strings.TrimSpace(strings.TrimSuffix(line, "\r"))
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+ if len(line) > redirectsMaxLine {
+ errs = append(errs, fmt.Errorf("line %d: longer than %d characters", n, redirectsMaxLine))
+ continue
+ }
+ rule, err := parseRedirectLine(line)
+ if err != nil {
+ errs = append(errs, fmt.Errorf("line %d: %v", n, err))
+ continue
+ }
+ if len(rs.rules) == redirectsMaxRules {
+ errs = append(errs, fmt.Errorf("line %d: more than %d rules", n, redirectsMaxRules))
+ continue
+ }
+ rule.line = n
+ rs.rules = append(rs.rules, rule)
+ }
+ return rs, errors.Join(errs...)
+}
+
+func parseRedirectLine(line string) (redirectRule, error) {
+ var fields []string
+ for _, f := range strings.Fields(line) {
+ if strings.HasPrefix(f, "#") {
+ break // the rest of the line is a comment
+ }
+ fields = append(fields, f)
+ }
+ var rule redirectRule
+ switch {
+ case len(fields) < 2:
+ return rule, errors.New("want a source and a destination")
+ case len(fields) > 3:
+ if strings.Contains(fields[3], "=") {
+ return rule, fmt.Errorf("conditions such as %q are not supported", fields[3])
+ }
+ return rule, fmt.Errorf("unexpected %q after the status", fields[3])
+ }
+
+ rule.status = 301
+ if len(fields) == 3 {
+ st := fields[2]
+ if s, ok := strings.CutSuffix(st, "!"); ok {
+ rule.force, st = true, s
+ }
+ code, err := strconv.Atoi(st)
+ if err != nil || !(isRedirect(code) || code == 200 || code == 404) {
+ return rule, fmt.Errorf("status %q: want 301, 302, 303, 307, 308, 200 or 404, optionally followed by !", fields[2])
+ }
+ rule.status = code
+ }
+
+ src := fields[0]
+ if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
+ u, err := url.Parse(src)
+ if err != nil || u.Host == "" || u.User != nil || u.Port() != "" || u.RawQuery != "" || u.Fragment != "" {
+ return rule, fmt.Errorf("source %q: want a URL with only a host and a path", src)
+ }
+ rule.host = serverName(u.Hostname()) // as requestHost normalizes the request's
+ src = u.Path
+ if src == "" {
+ src = "/"
+ }
+ }
+ if !strings.HasPrefix(src, "/") {
+ return rule, fmt.Errorf("source %q: want a path starting with / or an http(s) URL", fields[0])
+ }
+ if strings.ContainsAny(src, "?#") {
+ return rule, fmt.Errorf("source %q: query strings and fragments are not matched", fields[0])
+ }
+ names := map[string]bool{}
+ trimmed := strings.Trim(src, "/")
+ for _, seg := range strings.Split(trimmed, "/") {
+ switch {
+ case seg == "":
+ if trimmed != "" {
+ return rule, fmt.Errorf("source %q: empty path segment", fields[0])
+ }
+ case rule.splat:
+ return rule, fmt.Errorf("source %q: * must be the last segment", fields[0])
+ case seg == "*":
+ rule.splat = true
+ case strings.Contains(seg, "*"):
+ return rule, fmt.Errorf("source %q: * must be a whole segment", fields[0])
+ case strings.HasPrefix(seg, ":"):
+ name := seg[1:]
+ if !validPlaceholder(name) || name == "splat" {
+ return rule, fmt.Errorf("source %q: bad placeholder %q", fields[0], seg)
+ }
+ if names[name] {
+ return rule, fmt.Errorf("source %q: placeholder %q appears twice", fields[0], seg)
+ }
+ names[name] = true
+ rule.segs = append(rule.segs, seg)
+ default:
+ // Requests match decoded, as files are found, so a literal
+ // segment is decoded here to meet them: "/caf%C3%A9" matches a
+ // request for /café. A decoded "/" would be two segments, and
+ // no request path has one inside a segment.
+ dec, err := url.PathUnescape(seg)
+ if err != nil || strings.Contains(dec, "/") {
+ return rule, fmt.Errorf("source %q: bad percent-encoding in %q", fields[0], seg)
+ }
+ if strings.HasPrefix(dec, ":") {
+ return rule, fmt.Errorf("source %q: %q would read as a placeholder", fields[0], seg)
+ }
+ if dec == "." || dec == ".." {
+ return rule, fmt.Errorf("source %q: dot segments never match", fields[0])
+ }
+ rule.segs = append(rule.segs, dec)
+ }
+ }
+
+ dst := fields[1]
+ switch {
+ case strings.HasPrefix(dst, "http://") || strings.HasPrefix(dst, "https://"):
+ if !isRedirect(rule.status) {
+ return rule, fmt.Errorf("destination %q: a %d serves a file from this site, so it takes a path, not a URL", dst, rule.status)
+ }
+ u, err := url.Parse(dst)
+ if err != nil || u.Host == "" || u.User != nil {
+ return rule, fmt.Errorf("destination %q: not a usable URL", dst)
+ }
+ i := len(u.Scheme) + len("://") + len(u.Host)
+ if !strings.EqualFold(dst[:i], u.Scheme+"://"+u.Host) {
+ return rule, fmt.Errorf("destination %q: not a usable URL", dst)
+ }
+ rule.prefix, rule.to = dst[:i], dst[i:]
+ if rule.to != "" && !strings.ContainsAny(rule.to[:1], "/?#") {
+ return rule, fmt.Errorf("destination %q: not a usable URL", dst)
+ }
+ case strings.HasPrefix(dst, "//") || strings.HasPrefix(dst, "/\\"):
+ return rule, fmt.Errorf("destination %q: name another host with https://", dst)
+ case strings.HasPrefix(dst, "/"):
+ rule.to = dst
+ default:
+ return rule, fmt.Errorf("destination %q: want a path starting with / or an http(s) URL", dst)
+ }
+ return rule, nil
+}
+
+// validPlaceholder: a placeholder name is letters, digits and underscores.
+func validPlaceholder(name string) bool {
+ if name == "" {
+ return false
+ }
+ for _, c := range name {
+ if !(c == '_' || c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
+ return false
+ }
+ }
+ return true
+}
+
+// match reports whether the rule applies to a request for host whose cleaned
+// path splits into reqSegs, and returns what the source captured.
+func (rule *redirectRule) match(host string, reqSegs []string) (map[string]string, bool) {
+ if rule.host != "" && rule.host != host {
+ return nil, false
+ }
+ if len(reqSegs) < len(rule.segs) || !rule.splat && len(reqSegs) != len(rule.segs) {
+ return nil, false
+ }
+ var caps map[string]string
+ for i, seg := range rule.segs {
+ if strings.HasPrefix(seg, ":") {
+ if caps == nil {
+ caps = map[string]string{}
+ }
+ caps[seg[1:]] = reqSegs[i]
+ } else if seg != reqSegs[i] {
+ return nil, false
+ }
+ }
+ if rule.splat {
+ if caps == nil {
+ caps = map[string]string{}
+ }
+ caps["splat"] = strings.Join(reqSegs[len(rule.segs):], "/")
+ }
+ return caps, true
+}
+
+// expand substitutes the captures into the destination template. With escape
+// set, values are percent-encoded for a Location header: per segment in the
+// path, as a query value after "?". Without it they go in raw, for a rewrite's
+// file lookup. A ":name" the source did not capture stays as written.
+func expand(t string, caps map[string]string, escape bool) string {
+ var b strings.Builder
+ inQuery := false
+ for i := 0; i < len(t); {
+ c := t[i]
+ if c == '?' || c == '#' {
+ inQuery = true
+ }
+ if c != ':' {
+ b.WriteByte(c)
+ i++
+ continue
+ }
+ j := i + 1
+ for j < len(t) && validPlaceholder(t[j:j+1]) {
+ j++
+ }
+ v, ok := caps[t[i+1:j]]
+ if !ok {
+ b.WriteString(t[i:j])
+ i = j
+ continue
+ }
+ switch {
+ case !escape:
+ b.WriteString(v)
+ case inQuery:
+ b.WriteString(url.QueryEscape(v))
+ default:
+ parts := strings.Split(v, "/")
+ for k, p := range parts {
+ parts[k] = url.PathEscape(p)
+ }
+ b.WriteString(strings.Join(parts, "/"))
+ }
+ i = j
+ }
+ return b.String()
+}
+
+// redirectAction is what a matched rule asks the handler to do.
+type redirectAction struct {
+ status int
+ location string // a redirect's Location
+ file string // a rewrite's cleaned, rooted path within the tree
+}
+
+// resolve finds the first rule that applies to r's host and the cleaned path
+// p. exists is whether the tree has a file at p, which shadows every rule not
+// forced. rawPath and rawQuery are the request's own, used to refuse a
+// redirect to the URL being requested, which would loop.
+func (rs *redirectRules) resolve(host, p, rawPath, rawQuery string, exists bool) (redirectAction, bool) {
+ if rs == nil || len(rs.rules) == 0 {
+ return redirectAction{}, false
+ }
+ var reqSegs []string
+ if p != "/" {
+ reqSegs = strings.Split(p[1:], "/")
+ }
+ for i := range rs.rules {
+ rule := &rs.rules[i]
+ if exists && !rule.force {
+ continue
+ }
+ caps, ok := rule.match(host, reqSegs)
+ if !ok {
+ continue
+ }
+ if sp := caps["splat"]; sp != "" && strings.HasSuffix(rawPath, "/") {
+ // The cleaned path lost it, and a page moved from /about/ to
+ // /about resolves its relative links against the wrong directory.
+ caps["splat"] = sp + "/"
+ }
+ if !isRedirect(rule.status) {
+ // The template's own query and fragment are cut BEFORE the
+ // captures go in: a decoded capture can hold "?" or "#", and it
+ // is part of the file's name, not the end of it.
+ to := rule.to
+ if k := strings.IndexAny(to, "?#"); k >= 0 {
+ to = to[:k]
+ }
+ return redirectAction{status: rule.status, file: path.Clean("/" + expand(to, caps, false))}, true
+ }
+ // loc is the destination after any scheme://host: path, query.
+ loc := expand(rule.to, caps, true)
+ frag := ""
+ if k := strings.IndexByte(loc, '#'); k >= 0 {
+ loc, frag = loc[:k], loc[k:]
+ }
+ if rule.prefix == "" {
+ // Cleaned here, as http.Redirect would clean it, so the loop
+ // check below sees the Location that is actually sent. Clean
+ // also folds the "//" an empty capture can leave at the front,
+ // which a browser would read as another host.
+ pth, q, hasQ := strings.Cut(loc, "?")
+ trailing := strings.HasSuffix(pth, "/")
+ pth = path.Clean("/" + pth)
+ if trailing && pth != "/" {
+ pth += "/"
+ }
+ loc = pth
+ if hasQ {
+ loc += "?" + q
+ }
+ }
+ if !strings.Contains(loc, "?") && rawQuery != "" {
+ loc += "?" + rawQuery
+ }
+ if rule.prefix == "" || hostOf(rule.prefix) == host {
+ pth, q, _ := strings.Cut(loc, "?")
+ if pth == "" {
+ pth = "/"
+ }
+ if u, err := url.Parse(pth); err == nil && u.Path == rawPath && q == rawQuery {
+ continue // a redirect to this very URL
+ }
+ }
+ return redirectAction{status: rule.status, location: rule.prefix + loc + frag}, true
+ }
+ return redirectAction{}, false
+}
+
+// hostOf is the host of a "scheme://host[:port]" prefix, normalized as
+// requestHost normalizes a request's.
+func hostOf(prefix string) string {
+ _, h, _ := strings.Cut(prefix, "://")
+ return requestHost(h)
+}
diff --git a/internal/edge/redirects_test.go b/internal/edge/redirects_test.go
new file mode 100644
index 000000000..8495a3537
--- /dev/null
+++ b/internal/edge/redirects_test.go
@@ -0,0 +1,319 @@
+package edge
+
+import (
+ "context"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/carlosframework/platform/internal/registry"
+ "github.com/carlosframework/platform/internal/release"
+)
+
+func TestParseRedirectsAcceptsTheSupportedForms(t *testing.T) {
+ rs, err := parseRedirects([]byte(`# a comment
+
+/old /new
+/blog/* /posts/:splat 302
+/docs/:slug /guide/:slug 307 # trailing comment
+/app/* /app/index.html 200
+/* /404.html 404
+/forced /elsewhere 301!
+https://www.example.com/* https://example.com/:splat 301!
+/ext https://example.org/x?y=1 308
+` + "/crlf /there 303\r\n"))
+ if err != nil {
+ t.Fatalf("parseRedirects: %v", err)
+ }
+ if _, err := parseRedirects([]byte("\ufeff/bom /ok\n")); err != nil {
+ t.Fatalf("a file starting with a byte-order mark: %v", err)
+ }
+ if got := len(rs.rules); got != 9 {
+ t.Fatalf("parsed %d rules, want 9", got)
+ }
+ if r := rs.rules[0]; r.status != 301 || r.force || r.line != 3 {
+ t.Fatalf("rule 0 = %+v, want a 301 from line 3, not forced", r)
+ }
+ if r := rs.rules[6]; r.host != "www.example.com" || !r.force || !r.splat || r.prefix != "https://example.com" {
+ t.Fatalf("host rule = %+v", r)
+ }
+}
+
+func TestParseRedirectsRefusesWhatItCannotServe(t *testing.T) {
+ for _, line := range []string{
+ "/only-a-source",
+ "/a /b 301 Country=us",
+ "/a /b 301 extra",
+ "/a /b 418",
+ "/a /b 200x",
+ "a /b",
+ "/caf%ZZ /c",
+ "/a%2Fb /c",
+ "/%3Aname /c",
+ "/a b",
+ "/a //evil.example",
+ `/a /\evil.example`,
+ "/a/*/b /c",
+ "/a* /c",
+ "/:splat /c",
+ "/:a/:a /c",
+ "/:a-b /c",
+ "/a//b /c",
+ "/a?x=1 /c",
+ "/a/../b /c",
+ "https://www.example.com:8443/ /c",
+ "/a https://example.org/ 200",
+ "/a https://example.org/ 404",
+ "/a ftp://example.org/",
+ "/a https:///nohost",
+ } {
+ rs, err := parseRedirects([]byte(line + "\n"))
+ if err == nil {
+ t.Errorf("parseRedirects(%q) = %+v, want an error", line, rs.rules)
+ continue
+ }
+ if !strings.Contains(err.Error(), "line 1:") {
+ t.Errorf("parseRedirects(%q) error %q does not name the line", line, err)
+ }
+ if len(rs.rules) != 0 {
+ t.Errorf("parseRedirects(%q) kept %d rules", line, len(rs.rules))
+ }
+ }
+}
+
+func TestParseRedirectsKeepsTheGoodLinesAroundABadOne(t *testing.T) {
+ rs, err := parseRedirects([]byte("/a /b\n/c /d 999\n/e /f\n"))
+ if err == nil || !strings.Contains(err.Error(), "line 2:") {
+ t.Fatalf("err = %v, want one naming line 2", err)
+ }
+ if len(rs.rules) != 2 || rs.rules[1].line != 3 {
+ t.Fatalf("rules = %+v, want lines 1 and 3", rs.rules)
+ }
+}
+
+func TestParseRedirectsLimits(t *testing.T) {
+ if _, err := parseRedirects([]byte(strings.Repeat("#", redirectsMaxBytes+1))); err == nil {
+ t.Fatal("an oversized file parsed")
+ }
+ var b strings.Builder
+ for range redirectsMaxRules + 1 {
+ b.WriteString("/a /b\n")
+ }
+ rs, err := parseRedirects([]byte(b.String()))
+ if err == nil || len(rs.rules) != redirectsMaxRules {
+ t.Fatalf("%d rules, err %v: want %d rules and an error", len(rs.rules), err, redirectsMaxRules)
+ }
+}
+
+func TestPackDirRefusesABadRedirectsFile(t *testing.T) {
+ dir := writeTree(t, map[string]string{
+ "index.html": "home",
+ "_redirects": "/ok /fine\n/a /b 418\n",
+ })
+ _, err := PackDir(dir)
+ if err == nil || !strings.Contains(err.Error(), "_redirects") || !strings.Contains(err.Error(), "line 2:") {
+ t.Fatalf("PackDir = %v, want a refusal naming _redirects line 2", err)
+ }
+ // Only the root file is the rules file; one deeper is content.
+ if _, err := PackDir(writeTree(t, map[string]string{"sub/_redirects": "not rules"})); err != nil {
+ t.Fatalf("PackDir with a nested _redirects: %v", err)
+ }
+}
+
+// redirectFixture ships a site with a _redirects file behind a static
+// handler, and returns a client that does not follow redirects.
+func redirectFixture(t *testing.T, rules string) (*httptest.Server, *http.Client) {
+ t.Helper()
+ store := release.NewDirStore(filepath.Join(t.TempDir(), "store"))
+ priv, pub := testKeys(t)
+ shipSite(t, store, "site", "v1", map[string]string{
+ "index.html": "home",
+ "old.html": "old page",
+ "forced.html": "forced page",
+ "style.css": "body{}",
+ "about/index.html": "about",
+ "app/index.html": "the app",
+ "docs/page.html": "a doc",
+ "404.html": "<!doctype html><p>custom not found",
+ "_redirects": rules,
+ })
+ if err := release.Promote(context.Background(), store, priv, release.OpsAccount, "site", "canary/x", "v1"); err != nil {
+ t.Fatalf("Promote: %v", err)
+ }
+ rt := registry.Route{Host: "site.example.com", Kind: "static", App: "site", Channel: "canary/x", Status: "ready"}
+ h := newStaticHandler(rt, store, pub, filepath.Join(t.TempDir(), "cache"))
+ srv := httptest.NewServer(h)
+ t.Cleanup(srv.Close)
+ client := &http.Client{CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }}
+ return srv, client
+}
+
+type redirectResult struct {
+ code int
+ location string
+ body string
+ resp *http.Response
+}
+
+func fetch(t *testing.T, c *http.Client, method, base, host, path string) redirectResult {
+ t.Helper()
+ req, err := http.NewRequest(method, base+path, nil)
+ if err != nil {
+ t.Fatalf("new request: %v", err)
+ }
+ req.Host = host
+ resp, err := c.Do(req)
+ if err != nil {
+ t.Fatalf("%s %s: %v", method, path, err)
+ }
+ defer resp.Body.Close()
+ b, _ := io.ReadAll(resp.Body)
+ return redirectResult{resp.StatusCode, resp.Header.Get("Location"), string(b), resp}
+}
+
+const siteRules = `
+/old.html /new.html 301
+/forced.html /elsewhere 302!
+/blog/* /posts/:splat 301
+/docs/:slug /guide/:slug?from=:slug 302
+/app/* /app/index.html 200
+https://www.example.com/* https://site.example.com/:splat 301!
+/loop/ /loop 301
+/ext https://example.org/x?y=1 307
+/empty/* /:splat/tail 301
+/frag /target#section 302
+/slash/* /slash/:splat/ 301!
+/caf%C3%A9 /cafe 301
+/files/:n /docs/:n.html 200
+/missing-rewrite /no/such/file 200
+/* /404.html 404
+`
+
+func TestStaticRedirects(t *testing.T) {
+ srv, c := redirectFixture(t, siteRules)
+ const host = "site.example.com"
+ for _, tc := range []struct {
+ host, path string
+ code int
+ location string // checked when code is a redirect
+ body string // checked when code is not
+ }{
+ // A file at the path shadows a rule; "!" overrides that.
+ {host, "/old.html", 200, "", "old page"},
+ {host, "/forced.html", 302, "/elsewhere", ""},
+ // Splats, placeholders, query strings.
+ {host, "/blog/2024/hello", 301, "/posts/2024/hello", ""},
+ {host, "/blog/2024/hello?x=1", 301, "/posts/2024/hello?x=1", ""},
+ {host, "/blog/2024/", 301, "/posts/2024/", ""},
+ {host, "/blog", 301, "/posts/", ""},
+ {host, "/docs/a%20b", 302, "/guide/a%20b?from=a+b", ""},
+ // Paths match decoded, as files are found: %2F separates segments.
+ {host, "/docs/a%2Fb", 404, "", "<!doctype html><p>custom not found"},
+ {host, "/ext?a=b", 307, "https://example.org/x?y=1", ""},
+ {host, "/frag?q=1", 302, "/target?q=1#section", ""},
+ // An existing file outside the rules is untouched.
+ {host, "/docs/page.html", 200, "", "a doc"},
+ // A directory with an index exists; a path under it is rewritten.
+ {host, "/app/", 200, "", "the app"},
+ {host, "/app/settings/profile", 200, "", "the app"},
+ // A redirect to the URL requested is skipped, not looped.
+ {host, "/loop/", 301, "/loop", ""},
+ {host, "/loop", 404, "", "<!doctype html><p>custom not found"},
+ // An empty capture never yields a protocol-relative Location.
+ {host, "/empty/", 301, "/tail", ""},
+ // A trailing-slash rule stops once the slash is there.
+ {host, "/slash/x", 301, "/slash/x/", ""},
+ {host, "/slash/x/", 404, "", "<!doctype html><p>custom not found"},
+ // A percent-encoded source matches the decoded request.
+ {host, "/caf%C3%A9", 301, "/cafe", ""},
+ // A "#" in a capture is part of the file name, not its end.
+ {host, "/files/page", 200, "", "a doc"},
+ {host, "/files/page.html%23", 404, "", "404 page not found\n"},
+ // The host rule matches its host only, and keeps the trailing slash.
+ {"www.example.com", "/about/", 301, "https://site.example.com/about/", ""},
+ {"WWW.Example.com.", "/style.css", 301, "https://site.example.com/style.css", ""},
+ {host, "/about/", 200, "", "about"},
+ // A rewrite to nothing is a plain 404; everything else hits /*.
+ {host, "/missing-rewrite", 404, "", "404 page not found\n"},
+ {host, "/nope", 404, "", "<!doctype html><p>custom not found"},
+ // The rules file is never served.
+ {host, "/_redirects", 404, "", "<!doctype html><p>custom not found"},
+ } {
+ got := fetch(t, c, "GET", srv.URL, tc.host, tc.path)
+ if got.code != tc.code {
+ t.Errorf("GET %s%s = %d (Location %q, body %q), want %d", tc.host, tc.path, got.code, got.location, got.body, tc.code)
+ continue
+ }
+ if tc.code >= 300 && tc.code < 400 {
+ if got.location != tc.location {
+ t.Errorf("GET %s%s Location = %q, want %q", tc.host, tc.path, got.location, tc.location)
+ }
+ } else if got.body != tc.body {
+ t.Errorf("GET %s%s body = %q, want %q", tc.host, tc.path, got.body, tc.body)
+ }
+ if v := got.resp.Header.Get("X-Carlos-Version"); v != "v1" {
+ t.Errorf("GET %s%s X-Carlos-Version = %q, want v1", tc.host, tc.path, v)
+ }
+ if cc := got.resp.Header.Get("Cache-Control"); cc != "no-cache" && tc.code != 404 {
+ t.Errorf("GET %s%s Cache-Control = %q, want no-cache", tc.host, tc.path, cc)
+ }
+ }
+
+ // The not-found page is typed and carries no validator.
+ got := fetch(t, c, "GET", srv.URL, host, "/nope")
+ if ct := got.resp.Header.Get("Content-Type"); !strings.HasPrefix(ct, "text/html") {
+ t.Errorf("404 page Content-Type = %q, want text/html", ct)
+ }
+ if et := got.resp.Header.Get("ETag"); et != "" {
+ t.Errorf("404 page ETag = %q, want none", et)
+ }
+ if head := fetch(t, c, "HEAD", srv.URL, host, "/nope"); head.code != 404 || head.body != "" {
+ t.Errorf("HEAD /nope = %d %q, want 404 with no body", head.code, head.body)
+ }
+}
+
+func TestStaticSPAFallbackLeavesAssetsAlone(t *testing.T) {
+ srv, c := redirectFixture(t, "/* /index.html 200\n")
+ const host = "site.example.com"
+ for path, want := range map[string]string{
+ "/style.css": "body{}",
+ "/about/": "about",
+ "/some/client/route": "home",
+ "/_redirects": "home",
+ } {
+ if got := fetch(t, c, "GET", srv.URL, host, path); got.code != 200 || got.body != want {
+ t.Errorf("GET %s = %d %q, want 200 %q", path, got.code, got.body, want)
+ }
+ }
+ // A rewritten page revalidates like the file it serves.
+ first := fetch(t, c, "GET", srv.URL, host, "/some/route")
+ tag := first.resp.Header.Get("ETag")
+ if tag == "" {
+ t.Fatal("rewritten page has no ETag")
+ }
+ req, _ := http.NewRequest("GET", srv.URL+"/other/route", nil)
+ req.Host = host
+ req.Header.Set("If-None-Match", tag)
+ resp, err := c.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ resp.Body.Close()
+ if resp.StatusCode != http.StatusNotModified {
+ t.Fatalf("conditional GET of a rewritten page = %d, want 304", resp.StatusCode)
+ }
+}
+
+func TestStaticWithoutRedirectsIsUnchanged(t *testing.T) {
+ _, _, _, srv := staticFixture(t)
+ c := &http.Client{CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }}
+ if got := fetch(t, c, "GET", srv.URL, "site.example.com", "/missing"); got.code != 404 || got.body != "404 page not found\n" {
+ t.Fatalf("GET /missing = %d %q, want the plain 404", got.code, got.body)
+ }
+ if got := fetch(t, c, "GET", srv.URL, "site.example.com", "/docs/page.html/index.html"); got.code != 404 {
+ t.Fatalf("GET under a file = %d, want 404", got.code)
+ }
+}
diff --git a/internal/edge/static.go b/internal/edge/static.go
index a61779f77..b1aa0ae07 100644
--- a/internal/edge/static.go
+++ b/internal/edge/static.go
@@ -28,6 +28,7 @@ import (
"path"
"path/filepath"
"sort"
+ "strconv"
"strings"
"sync"
"sync/atomic"
@@ -96,6 +97,14 @@ func PackDir(dir string) ([]byte, error) {
if _, err := tw.Write(b); err != nil {
return nil, err
}
+ if name == redirectsFile {
+ // Refused here rather than skipped at the edge, so a rule the
+ // edge cannot use is an error the shipper sees, not a redirect
+ // that silently never happens.
+ if _, err := parseRedirects(b); err != nil {
+ return nil, fmt.Errorf("pack %s: %s: %w", dir, name, err)
+ }
+ }
}
if err := tw.Close(); err != nil {
return nil, err
@@ -123,10 +132,11 @@ type staticSite struct {
recheck time.Duration
mu sync.Mutex
- version string // currently served version; "" until first resolve
- checkedAt time.Time // when the pointer was last read
- digest string // the served tree's manifest digest
- name string // the served tree's directory: siteDirName(version, digest)
+ version string // currently served version; "" until first resolve
+ checkedAt time.Time // when the pointer was last read
+ digest string // the served tree's manifest digest
+ name string // the served tree's directory: siteDirName(version, digest)
+ redirects *redirectRules // the served tree's _redirects; nil when it has none
// ratchet is #253's per-box ratchet for this process's pointer reads
// (Edge.staticRatchet); nil admits every verified pointer.
@@ -152,7 +162,7 @@ func newStaticHandler(rt registry.Route, store release.Store, pub ed25519.Public
}
func (s *staticSite) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- dir, version, err := s.currentDir(r.Context())
+ tree, err := s.currentDir(r.Context())
if err != nil {
// Nothing has ever resolved here, so there is no version to name:
// this 502 stays unstamped rather than claiming a build it isn't
@@ -172,7 +182,7 @@ func (s *staticSite) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// stamps what is actually served, never the pointer's unreachable head.
// Set before any body is written, so it lands on the tree's own 404s too
// (the handler serving that tree IS a version).
- w.Header().Set("X-Carlos-Version", version)
+ w.Header().Set("X-Carlos-Version", tree.version)
// A page out of the tree is revalidated before it is reused, and so is
// the tree's own 404. With Last-Modified alone a browser GUESSES a
// freshness lifetime — typically a tenth of the file's age — and reuses
@@ -185,7 +195,15 @@ func (s *staticSite) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// from the errors it writes itself, a 416 or a 400: neither is a status a
// browser caches on a guess.)
w.Header().Set("Cache-Control", "no-cache")
- s.serveTree(dir, w, r)
+ s.serveTree(tree, w, r)
+}
+
+// servedTree is what one request is answered from: an extracted tree, the
+// version it holds, and the rules its _redirects declared. They travel
+// together so a request never pairs one version's files with another's rules.
+type servedTree struct {
+ dir, version string
+ redirects *redirectRules
}
// dir is where the tree named name lives once extracted.
@@ -216,7 +234,8 @@ func (s *staticSite) appDir() string {
// afterwards so ServeHTTP can stamp X-Carlos-Version without re-taking mu (and
// without the window in between, where a concurrent resolve could swap
// s.version and make the header describe a different tree than the one served).
-func (s *staticSite) currentDir(ctx context.Context) (dir, version string, err error) {
+// The tree's redirect rules come back with it for the same reason.
+func (s *staticSite) currentDir(ctx context.Context) (servedTree, error) {
s.mu.Lock()
defer s.mu.Unlock()
if s.version != "" && s.recheck > 0 && time.Since(s.checkedAt) < s.recheck {
@@ -224,7 +243,7 @@ func (s *staticSite) currentDir(ctx context.Context) (dir, version string, err e
// version, and stamping that version is honest by construction — the
// header names what the bytes are, not what the pointer may have
// advanced to since the last check.
- return s.dir(s.name), s.version, nil
+ return s.served(), nil
}
cp, err := release.Channel(ctx, s.store, s.pub, s.acct, s.app, s.channel)
if err != nil {
@@ -242,22 +261,48 @@ func (s *staticSite) currentDir(ctx context.Context) (dir, version string, err e
return s.stale(fmt.Errorf("swap current -> %s: %w", name, err))
}
s.version, s.digest, s.name = cp.Version, digest, name
+ s.redirects = s.loadRedirects(s.dir(name), cp.Version)
}
s.checkedAt = time.Now()
- return s.dir(s.name), s.version, nil
+ return s.served(), nil
+}
+
+// served is the tree currently being served. The caller holds mu.
+func (s *staticSite) served() servedTree {
+ return servedTree{dir: s.dir(s.name), version: s.version, redirects: s.redirects}
+}
+
+// loadRedirects parses the _redirects at the root of an extracted tree, once
+// per version swap. A tree without one has no rules. A line that does not
+// parse is logged and skipped: ship refuses such a tree (PackDir), so only a
+// tree packed some other way gets here, and its remaining rules still apply.
+func (s *staticSite) loadRedirects(dir, version string) *redirectRules {
+ data, err := os.ReadFile(filepath.Join(dir, redirectsFile))
+ if errors.Is(err, fs.ErrNotExist) {
+ return nil
+ }
+ if err != nil {
+ log.Printf("edge: static %s/%s: %s %s: %v — serving without its rules", s.app, s.channel, version, redirectsFile, err)
+ return nil
+ }
+ rules, err := parseRedirects(data)
+ if err != nil {
+ log.Printf("edge: static %s/%s: %s %s: %s", s.app, s.channel, version, redirectsFile, strings.ReplaceAll(err.Error(), "\n", "; "))
+ }
+ return rules
}
// stale is the degraded path: keep serving the version we already verified
// and extracted — and name it, so the response header describes the CACHED
// tree rather than the head this handler could not reach — or fail if there
// has never been one.
-func (s *staticSite) stale(err error) (string, string, error) {
+func (s *staticSite) stale(err error) (servedTree, error) {
if s.version == "" {
- return "", "", err
+ return servedTree{}, err
}
log.Printf("edge: static %s/%s: %v — still serving cached %s", s.app, s.channel, err, s.version)
s.checkedAt = time.Now() // back off: retry next recheck window, not next request
- return s.dir(s.name), s.version, nil
+ return s.served(), nil
}
// ensureExtracted makes the version's verified tree exist and returns its
@@ -398,7 +443,9 @@ func extractSiteTar(data []byte, dst string) error {
// serveTree answers one request from an extracted version dir:
// http.FileServer semantics with index.html for directories and a clean 404
-// (never a directory listing) when neither the file nor an index exists.
+// (never a directory listing) when neither the file nor an index exists —
+// after the tree's _redirects (redirects.go) has had its say. A rule applies
+// only where no file answers the path, unless it is forced.
//
// It serves the file itself rather than through http.ServeFile, because
// ServeFile offers the file's mtime as Last-Modified and answers
@@ -412,7 +459,7 @@ func extractSiteTar(data []byte, dst string) error {
// The validator is the file's content (tagCache). What ServeFile did besides
// is kept by hand: the refusal of a ".." element, and the redirect off a
// trailing /index.html onto its directory.
-func (s *staticSite) serveTree(dir string, w http.ResponseWriter, r *http.Request) {
+func (s *staticSite) serveTree(tree servedTree, w http.ResponseWriter, r *http.Request) {
if containsDotDot(r.URL.Path) {
// The Clean below would resolve it harmlessly inside the tree; this
// keeps the answer ServeFile always gave such a path.
@@ -421,13 +468,12 @@ func (s *staticSite) serveTree(dir string, w http.ResponseWriter, r *http.Reques
}
// Clean rooted at "/" so no request path can climb out of the tree.
p := path.Clean("/" + r.URL.Path)
- fp := filepath.Join(dir, filepath.FromSlash(p))
- fi, err := os.Stat(fp)
- if err == nil && fi.IsDir() {
- fp = filepath.Join(fp, "index.html")
- fi, err = os.Stat(fp)
+ fp, fi, err := lookupTree(tree.dir, p)
+ if act, ok := tree.redirects.resolve(requestHost(r.Host), p, r.URL.Path, r.URL.RawQuery, err == nil); ok {
+ s.serveRule(tree.dir, act, w, r)
+ return
}
- if err != nil || fi.IsDir() {
+ if err != nil {
http.NotFound(w, r)
return
}
@@ -442,6 +488,75 @@ func (s *staticSite) serveTree(dir string, w http.ResponseWriter, r *http.Reques
w.WriteHeader(http.StatusMovedPermanently)
return
}
+ s.serveFile(fp, p, fi, w, r)
+}
+
+// lookupTree finds the file that answers the cleaned path p in an extracted
+// tree: the file itself, or a directory's index.html. The _redirects file is
+// configuration and answers nothing.
+func lookupTree(dir, p string) (string, os.FileInfo, error) {
+ if p == "/"+redirectsFile {
+ return "", nil, fs.ErrNotExist
+ }
+ fp := filepath.Join(dir, filepath.FromSlash(p))
+ fi, err := os.Stat(fp)
+ if err == nil && fi.IsDir() {
+ fp = filepath.Join(fp, "index.html")
+ fi, err = os.Stat(fp)
+ }
+ if err == nil && fi.IsDir() {
+ err = fs.ErrNotExist
+ }
+ return fp, fi, err
+}
+
+// serveRule answers with what a matched _redirects rule asked for: a
+// redirect, or another file of the tree served at this URL — with a 200 for a
+// rewrite and a 404 for a not-found page. A rewrite to a path with no file is
+// a plain 404; rules are not consulted a second time.
+func (s *staticSite) serveRule(dir string, act redirectAction, w http.ResponseWriter, r *http.Request) {
+ if act.location != "" {
+ http.Redirect(w, r, act.location, act.status)
+ return
+ }
+ fp, fi, err := lookupTree(dir, act.file)
+ if err != nil {
+ http.NotFound(w, r)
+ return
+ }
+ if act.status == http.StatusOK {
+ s.serveFile(fp, act.file, fi, w, r)
+ return
+ }
+ // A not-found page carries no validator and answers no conditional or
+ // range request: ServeContent knows only how to succeed.
+ f, err := os.Open(fp)
+ if err != nil {
+ http.NotFound(w, r)
+ return
+ }
+ defer f.Close()
+ ctype := mime.TypeByExtension(filepath.Ext(fp))
+ if ctype == "" {
+ var head [512]byte
+ n, _ := io.ReadFull(f, head[:])
+ ctype = http.DetectContentType(head[:n])
+ if _, err := f.Seek(0, io.SeekStart); err != nil {
+ http.NotFound(w, r)
+ return
+ }
+ }
+ w.Header().Set("Content-Type", ctype)
+ w.Header().Set("Content-Length", strconv.FormatInt(fi.Size(), 10))
+ w.WriteHeader(act.status)
+ if r.Method != http.MethodHead {
+ io.Copy(w, f)
+ }
+}
+
+// serveFile sends the tree file at fp, whose tree path is p (for logs), with
+// a content tag and net/http's conditional and range handling.
+func (s *staticSite) serveFile(fp, p string, fi os.FileInfo, w http.ResponseWriter, r *http.Request) {
f, err := os.Open(fp)
if err != nil {
// ServeFile's mapping, kept: a file that vanished since the Stat is