carlos / platform Public

spec: edge asset cache rev 3 (carlos deploy declares too)

Summary

carlos deploy takes --assets/--no-assets, the .carlos/config project default gains an assets list, and the console ship door checks the grammar itself.
claude agent, operated by paul@keymail.dev 80a533540e8de40afcb08f46f35b3d1f03f42bfb parent 4e9e433
1 file changed, +36 −5
  • docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md +36 −5

CI cancelled — superseded by a newer push — 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/smithy-go v1.28.1
go: downloading github.com/aws/aws-sdk-go-v2 v1.44.0
go: downloading golang.org/x/net v0.56.0
go: downloading github.com/superfly/ltx v0.5.1
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 golang.org/x/crypto v0.53.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/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/sts v1.45.6
go: downloading github.com/aws/aws-sdk-go-v2/service/autoscaling v1.74.0
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/credentials v1.19.6
go: downloading golang.org/x/sys v0.46.0
go: downloading github.com/pion/logging v0.2.4
go: downloading github.com/pion/turn/v4 v4.1.4
go: downloading github.com/pion/stun/v3 v3.0.1
go: downloading github.com/aws/aws-sdk-go-v2/service/sesv2 v1.66.6
go: downloading github.com/pierrec/lz4/v4 v4.1.22
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/internal/configsources v1.4.40
go: downloading github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.40
go: downloading golang.org/x/text v0.38.0
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/checksum v1.9.23
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31
go: downloading github.com/prometheus/client_golang v1.17.0
go: downloading github.com/aws/aws-sdk-go-v2/service/ec2 v1.324.0
go: downloading modernc.org/sqlite v1.55.0
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/sync v0.21.0
go: downloading github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.18
go: downloading github.com/lmittmann/tint v1.1.3
go: downloading github.com/mattn/go-isatty v0.0.20
go: downloading github.com/pierrec/lz4/v4 v4.1.22
go: downloading github.com/prometheus/client_golang v1.17.0
go: downloading github.com/pion/turn/v4 v4.1.4
go: downloading github.com/klauspost/compress v1.18.0
go: downloading golang.org/x/sync v0.21.0
go: downloading github.com/pion/transport/v3 v3.0.8
go: downloading github.com/pion/dtls/v3 v3.0.7
go: downloading github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.18
go: downloading golang.org/x/text v0.38.0
go: downloading github.com/pion/randutil v0.1.0
go: downloading github.com/pion/transport/v4 v4.0.1
go: downloading github.com/pion/randutil v0.1.0
go: downloading github.com/pion/transport/v4 v4.0.1
go: downloading github.com/pion/dtls/v3 v3.0.7
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/sso v1.30.8
go: downloading github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16
go: downloading github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12
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/feature/ec2/imds v1.18.16
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/aws/aws-sdk-go-v2/internal/ini v1.8.4
go: downloading github.com/beorn7/perks v1.0.1
go: downloading github.com/cespare/xxhash/v2 v2.3.0
go: downloading github.com/prometheus/common v0.45.0
go: downloading github.com/prometheus/procfs v0.12.0
go: downloading google.golang.org/protobuf v1.36.10
go: downloading github.com/prometheus/client_model v0.5.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 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 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/google/uuid v1.6.0
go: downloading github.com/dustin/go-humanize v1.0.1
go: downloading modernc.org/mathutil v1.7.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
go: downloading github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec

amadan: job cancelled — a newer push superseded it
diff --git a/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md b/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md
index aa5a40a21..07dd0347b 100644
--- a/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md
+++ b/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md
@@ -1,6 +1,6 @@
# Edge asset cache: versioned build assets served from every edge
-Branch `edge-asset-cache`. Status: rev 2, 2026-10-24, builder check folded in; rulings R1 and R2 (§13). Awaiting Paul's approval, then Astra review.
+Branch `edge-asset-cache`. Status: rev 3, 2026-10-24, builder check folded in; rulings R1 to R3 (§13). Awaiting Paul's approval, then Astra review.
Related: media roadmap (`2026-09-07-media-platform-roadmap-design.md`),
which this does not amend; #368 (the shared cache stores 206 responses).
@@ -76,7 +76,8 @@ untouched and still next in that line; §11 notes what it can reuse from here.
**Goals**
-- G1. An app declares path prefixes as build assets once, at ship time.
+- G1. An app declares path prefixes as build assets once, at ship or
+ deploy time, and a saved project default keeps declaring them.
- G2. Any edge that receives a request under a declared prefix serves it
from its own disk after the first fill, with no backhaul and no request
to the app.
@@ -132,6 +133,27 @@ Old binaries ignore the key (`encoding/json` drops unknown top-level keys
on the manifest read path, `release.go:95-100`). `kind:static` releases
refuse the flag; they are already everywhere.
+**`carlos deploy` declares the same way** (Paul, 2026-10-24). Deploy is
+ship, promote and watch in one verb, and its ship half is `runShipAPI`
+(`deploy.go:1-6`, `:334`), which already carries `--capabilities`. So:
+
+- `carlos deploy --assets /assets/` takes the same repeatable flag with
+ the same grammar and passes it to the ship half.
+- The project-defaults block in `.carlos/config` (`config.go:58-65`:
+ `app`, `kind`, `artifact`, `target`) gains `"assets": ["/assets/"]`, so
+ a zero-arg `carlos deploy` keeps declaring on every release. A flag on
+ the command line replaces the saved list for that run; `--no-assets`
+ ships with none. Without either, a release declares nothing, and since
+ paths come from the serving release (§4.1), deploying a build without
+ them turns catching off for it. That is why the saved default matters.
+- The console's ship door (API mode) puts the block in the manifest it
+ writes and checks the same grammar itself. It does not trust the CLI.
+- Deploy's watch probes the app's own URL for `X-Carlos-Version`. `/` can
+ never be declared, so the watch never lands on a caught path and a hit
+ can never answer it with a stale version (§5.5).
+- `--kind static` refuses `--assets` and ignores a saved list, saying so
+ once.
+
Why the release manifest and not the instance record: the paths an app
builds assets under change with its build tooling, and a rollback to a
build that used `/static/` must catch `/static/`, not the newer build's
@@ -455,8 +477,10 @@ any touched package; a test runs the full fill/hit/purge path against
check, flagship boxes, flagship console. Arm one dev box far from a test
app's home, prove the gate (§12), arm the rest of dev, then flagship one
box at a time, a small box (US or AU) first to watch disk.
-- **CLI.** `carlos ship --assets`, `carlos assets purge`, `carlos assets
- status`, `carlos instances update --assets` get `docs/site/cli.md`
+- **CLI.** `carlos ship --assets`, `carlos deploy --assets` and
+ `--no-assets`, the `assets` project default, `carlos assets purge`,
+ `carlos assets status`, `carlos instances update --assets` get
+ `docs/site/cli.md`
headings (docsite gate). Every user-facing string goes through copy
review before it is written.
- **Census.** Touching `internal/provision` and `internal/host` likely
@@ -505,7 +529,10 @@ routes. Attacks first:
- one app requesting unbounded unique query strings evicts its own entries
before another app's, and no fill starts under 15% free disk;
- a hibernating route's caught hit does not wake it;
-- `/` and the other bad prefixes are refused at ship.
+- `/` and the other bad prefixes are refused at ship, at deploy and by
+ the console's ship door with a CLI that skipped the check;
+- a zero-arg `carlos deploy` with a saved `assets` default declares it;
+ `--assets` replaces it for the run; `--no-assets` declares none.
Then, on dev: an asset requested through the far region's edge is a
`miss` once and a `hit` after, the home box's access log shows one request
@@ -518,6 +545,7 @@ On flagship, the same from a small box with the disk figures recorded.
instance can switch catching off and cannot declare paths (§3.2).
- **R2. Asset bytes count the same.** Every hit and fill is the app's
request and bytes out, against plan caps like any other response (§8).
+- **R3. It works with `carlos deploy`** as well as `carlos ship` (§3.1).
## 14. Review log
@@ -531,3 +559,6 @@ On flagship, the same from a small box with the disk figures recorded.
stamp and record-view bound; B9 import gate excluded. Purge status no
longer claims a far-edge acknowledgement it has no channel for.
Rulings R1 and R2 recorded.
+- rev 3, 2026-10-24: `carlos deploy` declares too (Paul): `--assets`,
+ `--no-assets`, the `.carlos/config` default, and the console ship door's
+ own check (§3.1).