carlos / platform Public

P7: approved copy for the door-key CLI and doors; the edge-local door in the CLI reference (#175)

Task 8 of the P7 plan. Every string as approved in copy review on
2026-09-29, verbatim: the door-key usage prose, the one-time warning, the
flag help, the console's door-key errors, the door's refusal, and a new
passage on the edge-local door in docs/site/cli.md's carlos instances
section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Human

Prompt

Approved (P7 copy review, 19 strings)

From this commit's own message. The prompt ledger has no entries for it — which is what a squashed, rebased or amended commit looks like, and what every commit looks like in a repo without a ledger.

Summary

P7 Task 8: approved copy and the docs passage.
claude agent, operated by paul@keymail.dev 5b9a6ae53863971682728434bd7d660b71ca2b52 parent acddcd4
3 files changed, +20 −10
  • docs/site/cli.md +14 −0
  • doorkeys_cli.go +5 −9
  • internal/console/apidoorkeys.go +1 −1

CI passed — run details

diff --git a/docs/site/cli.md b/docs/site/cli.md
index cedeb32f0..c173c7929 100644
--- a/docs/site/cli.md
+++ b/docs/site/cli.md
@@ -641,6 +641,20 @@ would reach that quota between them.
`--private-storage=false` turns the policy off. Instances created before it
keep the bindings and buckets they already have.
+**The edge-local door.** Some apps run their own processes on a box beside their instances: a router that serves many customers under one domain, a relay, a sync loop. Dialling each instance's socket directly stops working when an instance sleeps, moves to another box, or changes where its socket lives. The edge-local door is one socket those processes dial instead, wherever the instance is.
+
+```sh
+carlos instances door-key create --app hello --label router
+curl --unix-socket /run/carlos/edge-local.sock \
+ -H 'X-Carlos-Door-Key: cdk1.…' https://acme.hello.bab.oncarlos.com/
+```
+
+The door handles a request the way the edge handles one from the internet. It wakes a sleeping instance, forwards to the box that holds a moved one, and applies the password gate. It only reaches the key's own app. Any other host answers as unknown.
+
+The door believes no visitor address the caller sends. To your instance, a door request looks like one the box itself made to the public URL: it carries the box's public address, over `https`. If your app needs the visitor's address, have your process send it in a header your app signs and checks itself.
+
+The door is off until a box sets `CARLOS_EDGE_DOOR=/run/carlos/edge-local.sock` and its agent restarts, and the calling process's user must be enrolled with `carlos bootstrap --socket-consumer <user>`. A revoked key stops working within 30 seconds, or within 10 minutes on a box that cannot reach the bucket. A key's record the console has not re-signed for 72 hours stops working too, so a console that stays down that long closes the door.
+
Typed on a box with no sub-verb, `carlos instances` does something different:
it lists that box's own registry routes, backing and owning unit included.
Off the box, [carlos routes](/docs/cli#carlos-routes) answers the same
diff --git a/doorkeys_cli.go b/doorkeys_cli.go
index 40aafd064..2311dba97 100644
--- a/doorkeys_cli.go
+++ b/doorkeys_cli.go
@@ -33,17 +33,13 @@ const doorKeyUsage = `usage: carlos instances door-key create --app <a> [--label
--console <origin> console to act through (default: the only
logged-in one)
-Signed-in only (carlos auth login): a door key is a console-authorized
-write — the console's own database is the authority, and there is no
-direct-bucket equivalent.
+Signed in only (carlos auth login). A door key is written through a console, which keeps it in its own database. There is no direct-bucket form.
-create mints a fresh key for the app and prints it once. store it now,
-it cannot be shown again — a new "carlos instances door-key list" only
-ever shows the id and label, never the key itself.
+create mints a new key for the app and prints it once. Store it now: it cannot be shown again, and list only ever shows its id and label.
list shows the app's live keys: id, label and when each was created.
-revoke takes the id from list and stops that key working.
+revoke takes an id from list and stops that key working.
`
// ---- wire types -------------------------------------------------------------
@@ -148,7 +144,7 @@ func runInstancesDoorKey(args []string) {
func runInstancesDoorKeyCreate(args []string) {
fs := flag.NewFlagSet("instances door-key create", flag.ExitOnError)
app := appFlag(fs, "app to mint a door key for")
- label := fs.String("label", "", "short note stored alongside the key, shown by list (default: none)")
+ label := fs.String("label", "", "short note stored with the key and shown by list (default: none)")
consoleFlag := consoleOption(fs, "console to create through (default: the only logged-in one)")
acctRaw := fs.String("account", "", "account (sqid or name) the app belongs to (default: your only account, or the configured/chosen one)")
acctIDRaw := fs.String("account-id", "", "account sqid, matched as a sqid ONLY — supersedes --account")
@@ -172,7 +168,7 @@ func runInstancesDoorKeyCreate(args []string) {
// create follows for a standalone credential's password.
func printDoorKeyCreate(w io.Writer, res apiDoorKeyMint) {
fmt.Fprintln(w, res.Key)
- warnf(w, "shown once. store it now, it cannot be shown again.")
+ warnf(w, "shown once. store it now: it cannot be shown again")
}
func runInstancesDoorKeyList(args []string) {
diff --git a/internal/console/apidoorkeys.go b/internal/console/apidoorkeys.go
index 919ab5366..12f9a706a 100644
--- a/internal/console/apidoorkeys.go
+++ b/internal/console/apidoorkeys.go
@@ -38,7 +38,7 @@ const (
doorKeyPublishBudget = 20 * time.Second
)
-// Draft copy, pending review (plan Task 8).
+// Copy approved in review (plan Task 8), 2026-09-29.
const (
doorKeyReadOnlyMsg = "no signing key on this host. the console is running read-only"
doorKeyNoBucketMsg = "no deployment bucket on this host. the console is running without one"