🤖 The console, members and service credentials, limits and help
The last three docs pages: what each console page shows (written from internal/console handlers and templates, not from a browser), who can act on an account and how a credential a server holds is minted, rotated and revoked, and the rate limits a customer can meet with the numbers read from internal/console/authcli.go.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3 files changed,
+263
−0
src/docs/console.md+78 −0src/docs/limits.md+76 −0src/docs/members.md+109 −0
diff --git a/src/docs/console.md b/src/docs/console.md| new file mode 100644 |
| index 0000000..edbdd85 |
| --- /dev/null |
| +++ b/src/docs/console.md |
| @@ -0,0 +1,78 @@ |
| +# The console |
| + |
| +[console.carloku.com](https://console.carloku.com) is the same platform your |
| +terminal talks to, with a browser in front of it. You sign in with a passkey |
| +through Keymail, and the account you sign in to is the one |
| +`carlos auth login` connects a terminal to — the short code the CLI prints |
| +is approved in this browser. |
| + |
| +Most weeks you will barely open it. Deploys, config, logs and domains are all |
| +commands, and the console reads the records those commands write. Where it |
| +earns its place is the handful of jobs with no command behind them, and |
| +looking at an app somebody else on your team deployed. |
| + |
| +## What each page shows |
| + |
| +The bar across the top is per account: Apps, Fleet, Team and Settings, plus |
| +a switcher for moving between accounts when you belong to more than one. |
| + |
| +Apps is the list: each app's name, the address it answers on, a health chip, |
| +and the version live right now. A Trash link appears under it once something |
| +is in it. Opening an app gives you six tabs. |
| + |
| +Overview answers *is it up* in a sentence — the hostname, the running version, |
| +when it was published and by whom — then tiles for running copies, CPU and |
| +memory, web addresses and versions, and a left-to-right diagram of the |
| +channels a version travels through. [Channels](/docs/channels/) explains what |
| +that diagram is drawing. |
| + |
| +Versions is the release history. Every version you have shipped with its |
| +label, kind and build time and a promote control per row; underneath, the |
| +channels, what each holds, when a baked version goes live, and rollback. |
| +Canaries and semver tags sit at the bottom of the same page. |
| + |
| +Running is the copies themselves — address, health, version, channel, CPU, |
| +memory — and where the app is placed. A copy reading *asleep* is annotated |
| +there as waking on the next request, which [Restarting and |
| +sleeping](/docs/sleeping/) explains at more length. |
| + |
| +Logs is the window `carlos logs` prints, with a form for narrowing it. |
| +Activity is the other one: when, who, what, for every change made to this |
| +app. That record has no CLI verb, so the console is where you read it. |
| + |
| +Settings carries the app's name and rename, its domains with their DNS and |
| +TLS state alongside the attach form, its object store, its email |
| +configuration, a link through to the pipeline rules, and deletion. |
| + |
| +Back at account level, Fleet answers where this account's work actually runs: |
| +the shared platform boxes your apps are placed on, and any boxes of your own. |
| +Team and Settings are the subject of [Members and service |
| +credentials](/docs/members/). |
| + |
| +## Jobs that only happen here |
| + |
| +Approving a terminal is the first one you meet: `carlos auth login` prints a |
| +code, and nothing is granted until you type it into the console and confirm. |
| + |
| +The rest cluster around authority and permanence. Inviting a teammate, |
| +removing one and handing over ownership live on the Team page; minting, |
| +rotating and revoking a credential a server holds live on Account settings. |
| + |
| +Deletion is the other cluster. A deleted app goes to Trash and stays |
| +restorable for 30 days, keeping its releases, channels and history — |
| +restoring gives all of that back with no routes, so run |
| +`carlos add <host>` afterwards to serve it again. An owner can also destroy |
| +one immediately from the same page, which asks for the app's name typed out |
| +and then for your passkey. |
| + |
| +And when the day's app-creation budget is spent, the console's Create app |
| +form is the way through — it has no limit of its own. [Limits and getting |
| +help](/docs/limits/) has the numbers. |
| + |
| +## Your own settings |
| + |
| +Your address in the header opens a page listing every terminal signed in as |
| +you: where it signed in from, when, when it was last used, and when it |
| +expires. Each row has a revoke control. A laptop that went missing is the |
| +reason this page exists — revoking there kills that terminal's access |
| +without touching anyone else's. |
diff --git a/src/docs/limits.md b/src/docs/limits.md| new file mode 100644 |
| index 0000000..9c7f36d |
| --- /dev/null |
| +++ b/src/docs/limits.md |
| @@ -0,0 +1,76 @@ |
| +# Limits and getting help |
| + |
| +The numbers on this page exist to make a stolen token boring, not to ration |
| +ordinary work. You are unlikely to meet any of them by hand. An automated |
| +loop is another matter, so here they are, along with what the refusal looks |
| +like when one does bite. |
| + |
| +## Shipping |
| + |
| +Two uploads a minute, counted per terminal token. That is past what an honest |
| +build-test-ship loop can produce, since a build worth shipping takes longer |
| +than half a minute to make. |
| + |
| +A refusal is a 429 that names the wait in seconds and repeats it in a |
| +`Retry-After` header, so a script has a number to sleep on instead of a |
| +guess. The window is a minute for exactly that reason: the worst a refused |
| +caller is ever asked for is sixty seconds. Sleep, retry, carry on. |
| + |
| +Size is the other ship limit. One request carries 256 MiB across everything |
| +in it. A static release is a single archive; a binary release can carry up to |
| +eight targets at once, which is well past what a normal build produces. |
| + |
| +## Everything else you type |
| + |
| +Ordinary mutations — env, secrets, promotes, rollbacks, restarts, |
| +schedules — get thirty a minute, counted against your address and not your |
| +terminal, so opening a second shell does not double it. The instance verbs |
| +get sixty a minute on their own budget, because a burst of signups creating |
| +instances is the workload that API exists for. |
| + |
| +Claiming an app name is the tight one: ten attempts a day per address, and |
| +attempts, not successes, so refused tries count. A claimed name persists |
| +whether or not you ever use it, which is why this ceiling runs over a day and |
| +the others run over a minute. When you hit it, the message points at the |
| +console's Create app form, which has no limit of its own. That is the |
| +intended way through, not a loophole. |
| + |
| +## Credentials a server holds |
| + |
| +A credential minted with `carlos services` draws on its own budgets, larger |
| +than a person's and entirely separate: ten ships a minute, a hundred and |
| +twenty other writes a minute, a hundred app claims a day. A CI fleet cutting |
| +releases back to back therefore cannot starve the person who minted it, and |
| +the person's noisy afternoon cannot stop CI. |
| + |
| +The counters key on the credential's name, not its secret, so rotating one |
| +does not hand it a fresh budget. [Members and service |
| +credentials](/docs/members/) covers the rest of how they work. |
| + |
| +## Things that expire |
| + |
| +Logs go back seven days, with a size ceiling per app that drops the oldest |
| +hour first — [Logs](/docs/logs/) has the detail. A deleted app sits in Trash |
| +for thirty days before the platform destroys it. A terminal you have signed |
| +in stays valid for ninety days, and you can revoke it sooner from your own |
| +settings page. An invitation to join an account lasts seven days and works |
| +once. |
| + |
| +## When something is refused |
| + |
| +Read the message before anything else. The platform's refusals are written to |
| +be acted on: a rate limit names the seconds to wait, an app you cannot claim |
| +today names the console form that will let you, an attached domain that is |
| +not resolving prints the DNS record it wants. Very few need interpreting. |
| + |
| +After that, in rough order of how often it helps: `carlos <verb> -h` for the |
| +flags, [the CLI reference](https://carlosframework.com/docs/cli/) for the |
| +full page, and `carlos logs -app <app> -since 1h` for anything where your app |
| +started and then misbehaved. If a deploy looks stuck, check |
| +`X-Carlos-Version` on the live hostname, not the page in your browser — |
| +[Deploying](/docs/deploying/) explains why the header is the honest answer. |
| + |
| +If none of that gets you there, open an issue on |
| +[the carloku repo](https://github.com/carlosframework/carloku/issues). These |
| +pages are written and kept there, so a question worth asking is usually a |
| +page worth fixing, and both happen in the same place. |
diff --git a/src/docs/members.md b/src/docs/members.md| new file mode 100644 |
| index 0000000..69c98b1 |
| --- /dev/null |
| +++ b/src/docs/members.md |
| @@ -0,0 +1,109 @@ |
| +# Members and service credentials |
| + |
| +Two kinds of thing act on your apps. A person is a Keymail address with a |
| +role in the account. A credential is something a server holds — a CI runner, |
| +a job, a router — with its reach decided when it was minted and no human |
| +attached to it. They are administered in different places, and the mistakes |
| +they invite are different too. |
| + |
| +## People |
| + |
| +Invites go out from the Team page in the console, addressed to a Keymail |
| +address. The invitation lands in that person's Keymail Apps inbox rather than |
| +their email; the platform does not send mail to anyone. It works once and |
| +expires after seven days, and an owner can revoke a pending one before it is |
| +used. |
| + |
| +There are two roles. A member does the day-to-day work on the account's apps |
| +— deploy, promote, roll back, config, domains, logs. An owner does all of |
| +that plus everything that changes who can act or ends something permanently: |
| +inviting, removing a member, handing ownership over, deleting an app or the |
| +account, and every credential in the next section. Handing over ownership is |
| +a single transaction that steps you down to member as it lifts them up, so |
| +there is no moment with two owners or none. |
| + |
| +Some of those actions ask for your passkey again even though you are already |
| +signed in — removing a member, transferring ownership, deleting the account, |
| +approving a terminal or revoking one, minting or rotating a credential. The |
| +gesture has to be within the last five minutes. A session cookie proves who |
| +you are and not that you are still at the keyboard, which is the whole |
| +question when the action is irreversible. |
| + |
| +None of this has a `carlos` verb. Membership is console work, deliberately: |
| +the passkey prompt is the point of it, and a terminal has nowhere to put one. |
| + |
| +## Credentials a server holds |
| + |
| +```sh |
| +carlos services create -role publish -app hello ci-runner |
| +``` |
| + |
| +The secret prints once, right there in your terminal, and only its hash is |
| +stored. There is no page and no command that will show it to you again — a |
| +lost secret means rotating, not re-running. Get it into the app the way you |
| +get any credential in: |
| + |
| +```sh |
| +carlos secrets set -app hello CARLOS_SERVICE_TOKEN=<the secret> |
| +``` |
| + |
| +Pick the narrowest role that does the job. `publish` is the deploy loop: |
| +claim an app, ship, promote, roll back, manage hostnames, retire. `operate` |
| +is runtime work with no release authority — restart, config, logs. `instance` |
| +is the older app-token shape, kept for what already depends on it. `admin` is |
| +the union of those plus ledgers, storage and placement, and it includes |
| +granting object-store access, which mints a real IAM user. That last one is a |
| +handover, not a convenience. |
| + |
| +`-app hello` binds the credential to one app, which also means it cannot |
| +claim new ones. Leave it off for an account-wide credential. Either way the |
| +reach is fixed at mint: if you join another account next month, the |
| +credential you minted does not follow you there. It draws on its own rate |
| +budgets rather than yours, too — see [Limits](/docs/limits/) — and it never |
| +expires, so revoking is the control. |
| + |
| +## Rotate replaces, revoke stops |
| + |
| +This is the distinction to have straight before you need it. |
| + |
| +```sh |
| +carlos services rotate ci-runner |
| +``` |
| + |
| +Rotation mints a fresh secret and prints it once, keeping the name, the role, |
| +the scope and the history. The old secret is refused on the credential's |
| +*next* call — work already in flight is unaffected. That makes rotation a |
| +hygiene move, not an incident response. |
| + |
| +```sh |
| +carlos services revoke ci-runner |
| +``` |
| + |
| +Revocation is the hard stop: the credential's next call fails whatever secret |
| +it is holding. It is idempotent, so revoking a name twice, or one that never |
| +existed, answers the same way. And unlike minting and rotating, it does not |
| +ask for your passkey — one step, at whatever hour you find out. |
| + |
| +If you suspect a secret has leaked, revoke. Then mint a replacement under a |
| +new name if the work still needs doing. |
| + |
| +To see what exists: |
| + |
| +```sh |
| +carlos services ls |
| +``` |
| + |
| +Live credentials only — name, role, scope, when it was minted, when its |
| +current secret began, when it was last used, and who minted it. A revoked one |
| +is gone rather than listed as revoked, so this is inventory, not history. |
| + |
| +Every verb here is owner-only, and closed to service credentials themselves: |
| +no credential can mint, rotate or revoke any credential, not even its own. |
| +A leaked one therefore cannot quietly issue itself a quieter sibling. The |
| +console's Account settings page carries the same list, the same mint form, |
| +and rotate and revoke controls per row. |
| + |
| +Each verb takes `-account` when you belong to more than one; with a single |
| +account you can leave it off. |
| +[carlos services](https://carlosframework.com/docs/cli/#carlos-services) has |
| +the rest of the flags. |