carlos / platform Public

spec: plans, caps and packs, Paul's ruling on lapsed paid features

Grace, then disabled, then deleted 90 days later through the Trash.
Sponsored accounts never take this path. Lists the author's own calls
for Paul to check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 3085d6a2ac5218a111df6146046a4fc62244fc0c parent ea9b409
1 file changed, +58 −17
  • docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md +58 −17

CI passed — run details

CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
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]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok  	github.com/carlosframework/platform	328.159s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	2.840s
ok  	github.com/carlosframework/platform/cmd/moveprobe	93.241s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.713s
ok  	github.com/carlosframework/platform/internal/activator	251.242s
ok  	github.com/carlosframework/platform/internal/agentsock	0.015s
ok  	github.com/carlosframework/platform/internal/alerting	0.004s
ok  	github.com/carlosframework/platform/internal/analytics	0.020s
ok  	github.com/carlosframework/platform/internal/appconfig	2.519s
ok  	github.com/carlosframework/platform/internal/apperr	0.071s
ok  	github.com/carlosframework/platform/internal/applog	0.141s
ok  	github.com/carlosframework/platform/internal/awsinv	0.004s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.004s
ok  	github.com/carlosframework/platform/internal/boxid	0.371s
?   	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.008s
ok  	github.com/carlosframework/platform/internal/caps	0.013s
ok  	github.com/carlosframework/platform/internal/certwarm	0.002s
ok  	github.com/carlosframework/platform/internal/cgroup	0.703s
ok  	github.com/carlosframework/platform/internal/changelog	0.336s
ok  	github.com/carlosframework/platform/internal/claimactivation	0.818s
ok  	github.com/carlosframework/platform/internal/clamd	0.055s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
ok  	github.com/carlosframework/platform/internal/console	418.750s
?   	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.226s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.010s
ok  	github.com/carlosframework/platform/internal/dbverify	0.008s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.004s
ok  	github.com/carlosframework/platform/internal/docsite	0.005s
ok  	github.com/carlosframework/platform/internal/domains	2.372s
ok  	github.com/carlosframework/platform/internal/doorkey	0.006s
ok  	github.com/carlosframework/platform/internal/economics	0.006s
ok  	github.com/carlosframework/platform/internal/edge	44.953s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.011s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.135s
ok  	github.com/carlosframework/platform/internal/gate	1.655s
ok  	github.com/carlosframework/platform/internal/genselect	0.017s
ok  	github.com/carlosframework/platform/internal/geo	0.016s
ok  	github.com/carlosframework/platform/internal/geoip	0.039s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.156s
ok  	github.com/carlosframework/platform/internal/host	190.341s
ok  	github.com/carlosframework/platform/internal/hostactors	6.574s
ok  	github.com/carlosframework/platform/internal/hostactors/awsmembership	0.005s
ok  	github.com/carlosframework/platform/internal/hostbox	2.752s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.576s
ok  	github.com/carlosframework/platform/internal/hostenv	0.005s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.006s
ok  	github.com/carlosframework/platform/internal/hostexec	5.080s
ok  	github.com/carlosframework/platform/internal/hostfence	0.035s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.571s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.015s
ok  	github.com/carlosframework/platform/internal/hostguard	1.037s
ok  	github.com/carlosframework/platform/internal/hostops	1.654s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.003s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.002s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.477s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.135s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.002s
ok  	github.com/carlosframework/platform/internal/infraview	0.002s
ok  	github.com/carlosframework/platform/internal/instancectl	5.220s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.760s
ok  	github.com/carlosframework/platform/internal/instancefs	0.005s
ok  	github.com/carlosframework/platform/internal/instancekey	0.002s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.016s
ok  	github.com/carlosframework/platform/internal/layout	1.564s
ok  	github.com/carlosframework/platform/internal/lbhealth	0.004s
ok  	github.com/carlosframework/platform/internal/lease	9.925s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.364s
ok  	github.com/carlosframework/platform/internal/ledger	0.016s
ok  	github.com/carlosframework/platform/internal/mail	0.193s
ok  	github.com/carlosframework/platform/internal/maintenance	0.081s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.533s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.083s
ok  	github.com/carlosframework/platform/internal/metrics	0.006s
ok  	github.com/carlosframework/platform/internal/moves	21.189s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.840s
ok  	github.com/carlosframework/platform/internal/objstore	0.179s
ok  	github.com/carlosframework/platform/internal/ops	0.177s
ok  	github.com/carlosframework/platform/internal/park	0.213s
ok  	github.com/carlosframework/platform/internal/privatefile	0.004s
ok  	github.com/carlosframework/platform/internal/provision	39.027s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.011s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	5.558s
ok  	github.com/carlosframework/platform/internal/relay	7.240s
ok  	github.com/carlosframework/platform/internal/release	24.559s
ok  	github.com/carlosframework/platform/internal/rollout	3.894s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.005s
ok  	github.com/carlosframework/platform/internal/rootaws	0.007s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.059s
ok  	github.com/carlosframework/platform/internal/rootstate	0.027s
ok  	github.com/carlosframework/platform/internal/runs	0.579s
ok  	github.com/carlosframework/platform/internal/s3compat	0.006s
ok  	github.com/carlosframework/platform/internal/safefile	0.004s
ok  	github.com/carlosframework/platform/internal/schedule	2.537s
ok  	github.com/carlosframework/platform/internal/seeds	0.012s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	10.094s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	3.845s
ok  	github.com/carlosframework/platform/internal/status	0.060s
ok  	github.com/carlosframework/platform/internal/steering	0.006s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.245s
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.007s
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.005s
ok  	github.com/carlosframework/platform/internal/usage	0.005s
ok  	github.com/carlosframework/platform/internal/version	0.002s
ok  	github.com/carlosframework/platform/internal/vet	1.575s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
linux/arm64 carlos binary: 49807522 bytes (ceiling 52000000)
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
  minio: downloading https://github.com/minio/minio/releases/download/RELEASE.2025-09-07T16-13-09Z/minio.linux-arm64.RELEASE.2025-09-07T16-13-09Z
  minio: downloaded, digest verified
  mc: downloading https://github.com/minio/mc/releases/download/RELEASE.2025-08-13T08-35-41Z/mc.linux-arm64.RELEASE.2025-08-13T08-35-41Z
  mc: downloaded, digest verified
  minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
  buckets and scoped user ready
=== RUN   TestDeliveredContractMinio
=== RUN   TestDeliveredContractMinio/put
=== RUN   TestDeliveredContractMinio/get_round-trips_the_body
=== RUN   TestDeliveredContractMinio/list_shows_the_key
=== RUN   TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN   TestDeliveredContractMinio/delete_removes_it
=== RUN   TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
    --- PASS: TestDeliveredContractMinio/put (0.00s)
    --- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
    --- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
    --- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
    --- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
    --- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok  	github.com/carlosframework/platform/internal/objstore	0.030s
=== RUN   TestT4StorageProbeMinio
2026/10/20 18:07:20 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18E04F1D988B9D7F, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.01s)
PASS
ok  	github.com/carlosframework/platform/internal/host	0.029s
minio contract: PASS
diff --git a/docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md b/docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md
index 78bbbbd27..d96c54559 100644
--- a/docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md
+++ b/docs/superpowers/specs/2026-10-20-plans-caps-packs-design.md
@@ -46,7 +46,9 @@ Two deliveries, in this order:
- A free account that hits a cap can buy Personal (and packs) in the
console and is lifted immediately on payment.
- Every cap warns at 80%, allows a grace period, then refuses only the
- capped action. Apps keep serving. Data is never deleted by a cap.
+ capped action. Apps keep serving. Data is never deleted by a cap,
+ except what a lapsed or reduced paid account no longer pays for, and
+ only after grace, a 90-day disabled period and the Trash (9.2).
- Existing finite Carloku accounts stay sponsored: they act as paid, at
no charge (settled policy, #321).
- Plans are a general platform feature. A deployment with no plan catalog
@@ -65,7 +67,8 @@ Two deliveries, in this order:
### Anti-goals (would make this a failure even if it works)
- A surprise bill. Nothing is charged that the customer did not choose.
-- Losing a customer's data because of a cap or a lapsed payment.
+- Losing a customer's data without warning, or while they are still
+ paying for it.
- A free tier that can be farmed without limit, which makes every cap
meaningless.
- Carloku-specific code paths in the platform. carloku.com is a
@@ -87,6 +90,9 @@ By Paul in this conversation (2026-10-20), not to be reopened by review:
- **Every cap has a grace period with fair notice** before anything is cut
off.
- **Bandwidth** follows the same cap pattern.
+- **After a lapse, paid-only features get a grace period, are then
+ disabled, and are eventually deleted.** Agreed details: 7 days grace,
+ 90 days disabled with reminders, then the Trash's 30 days (9.2).
- Abuse guards: account farming, things that never sleep, and counting
everything that runs are in v1. Phishing defences are in v1 if free and
paid apps share a domain. Outbound abuse starts with an audit. Hidden
@@ -190,9 +196,10 @@ lowered limit would take something away from something running.
**Over the cap:** creation is refused. Nothing that already runs is
touched. An account can be over the cap only by losing a plan or a pack
(lapse, downgrade, reduction at renewal). Then: grace (section 6), and
-after grace the **newest** running things beyond the cap stop serving (a
-suspended state with a clear page) until the account is back under the
-cap or buys more. Nothing is deleted.
+after grace the **newest** running things beyond the cap are disabled
+(stop serving, show a clear page), and, if still over, deleted after the
+retention period (9.2). This is the one case where a cap leads to
+deletion.
### 5.2 Idle window, and things that never sleep
@@ -485,12 +492,32 @@ change plans or quantities; the console does.
A failed renewal follows Stripe's retry schedule. While Stripe retries, the
account keeps its paid limits. When the subscription ends, the account
drops to free limits and section 6 applies to every cap it is now over,
-with its normal grace. Nothing is deleted.
-
-Paid-only features already in use when an account drops to free
-(attached custom domains, fleets, schedules under 15 minutes, sidecars
-beyond the free cap) (codex 1.7): **open, for Paul** (see the end of
-this document).
+with its normal grace.
+
+**What the account no longer pays for is disabled, then deleted (Paul,
+2026-10-20, codex 1.7).** This covers everything a lapse, downgrade or
+pack reduction leaves beyond free (or the remaining) limits: running
+things beyond the cap and their data, sidecars beyond the cap, attached
+custom domains, and fleets. The steps:
+
+1. **Grace, 7 days** (section 6, including its notice rule). Everything
+ keeps working.
+2. **Disabled.** Stops serving; a custom domain stops routing; a fleet
+ takes no new work. Configuration and data are kept. Paying again (or
+ buying the pack back) re-enables it at once.
+3. **Deleted, 90 days after being disabled**, with reminders 30, 7 and 1
+ days before. Deletion goes through the existing Trash
+ (`release.TrashCooloff`, 30 days, `internal/release/trash.go:33`), so
+ it can still be restored for a month. Data is gone for good about four
+ months after the lapse at the earliest.
+
+Which running things are kept is the account's choice where it makes
+one before grace ends (pick which three stay); otherwise the newest go
+first. Schedules faster than the free minimum are not deleted: after
+grace they run at the free minimum interval.
+
+**Sponsored accounts are never on this path.** They keep the settled
+policy: paused at exhaustion, data kept at Carloku's expense.
### 9.3 Bands
@@ -626,15 +653,29 @@ codex 1.1 to 1.9; the three citation errors, in order, are codex 1.10 to
| 1.4 what counts as notice | section 6 |
| 1.5 storage unit | 5.4: per running thing |
| 1.6 boxes without quotas | 5.4: warn-only there |
-| 1.7 paid-only features after downgrade | open, for Paul |
+| 1.7 paid-only features after downgrade | 9.2, Paul's ruling: grace, disable, delete after 90 days via the Trash |
| 1.8 one free account per person after lapse | 8.1: creation only |
| 1.9 sponsored "current usage" measurement | 8.7 |
| 1.10 `CreateRecord` is not the instance choke point | 5.1 |
| 1.11 email caps are per sender | 5.6 |
| 1.12 house flag publishes no limits without a subscription | 9.4 |
-## Open, for Paul
-
-1. **Paid-only features after a downgrade** (codex 1.7). When a paid
- account lapses to free, what happens to custom domains, fleets and
- short schedules it already uses?
+## Calls made by the author, for Paul to check
+
+Not discussed with Paul; each is a proposal in the document:
+
+- All numbers in table 4.2 other than those marked (Paul), and pack
+ prices left blank.
+- Free gets 256 MB / 0.5 CPU (5.3).
+- No affordability bands in v1 (9.3).
+- One apps domain for free and paid, with paid-only custom domains,
+ abuse reports and takedown instead of a separate free domain (8.4).
+- Storage cap per running thing, not an account pool (5.4).
+- Notice rule: after-grace waits for received notice plus 3 days (6).
+- Awake-hours enforcement: throttle, close WebSockets, 15-minute
+ continuous-awake ceiling (5.2).
+- Sponsored limits from measured usage, busiest of the last three months
+ for recurring figures (8.7).
+- One free account per person checked at creation only (8.1).
+- Limits fail open on a box that has never read an account's record (7).
+- Outbound port 25 blocked for tenants on plan-governed deployments (8.5).