All docs

Tokens and scopes

Give CI and agents a credential narrower than your own account, and cap what it can spend.

Anything that is not you sitting at a terminal should have its own credential.

marina tokens create ci --preset deploy

A browser opens once so you can see the scopes and approve them, then the token is printed back to the terminal. It is printed once and never stored, because it is a credential for somewhere else: put it in MARINA_API_TOKEN, which always wins and reads no keychain, and is the path CI and agents should take.

Minting needs a browser on the same machine, so on a server with none, mint it on your laptop and copy it across. That is what you would do with a CI secret anyway. Listing and revoking are dashboard-only, on purpose: a leaked token cannot enumerate or revoke the others.

Presets

PresetCan doCan spend
readonlySee what you run, and the wallet balance.No
deployShip code to apps that already exist.No
agentBuild, deploy, config vars, databases.Yes
developerWhat marina login grants: everything above, plus domains and email.Yes

Two of them cannot grow your bill at all, whatever the thing holding them does. deploy is the one to reach for in CI: creating anything needs billing:spend, which it does not carry, so it ships new code and buys nothing.

It can still delete. Spending and destroying are different powers, and deploy lacks only the first, so a token holding it can tear down an app or a site.

There is no narrower write scope: vessel:write is create, deploy, scale and delete together, and nothing splits them. So a credential that can ship code can also remove what it ships to, which is the argument for giving it an expiry.

Scopes

A preset is a bundle. --scope names them directly, and the two combine:

marina tokens create narrow --scope vessel:read --scope vessel:write
ScopeGrants
account:readWho the credential belongs to. Every preset has it.
vessel:readSee apps.
vessel:writeCreate, deploy, scale, delete.
vessel:execRun a command inside a running app.
env:readRead config vars.
env:writeSet config vars.
db:readSee databases.
db:writeCreate and attach.
db:credentialsRead connection details, including the password.
site:read / site:writeStatic sites.
domain:read / domain:writeDomains and DNS.
email:read / email:writeEmail plans and mailboxes.
email:credentialsRead a mailbox password.
audit:readThe account’s activity log.
wallet:readBalance and invoices.
billing:spendSpend from the wallet.
billing:manageTop up and change payment details.

Four of these are deliberately awkward to get, and no preset includes any of them. env:read is not implied by env:write, and neither db:credentials nor email:credentials is implied by the write scope beside it, because being able to configure something and being able to read every secret it holds are different powers. vessel:exec is a shell inside your running app, which is every secret at once, so it is its own scope too.

No token can carry the permissions that manage the account itself. Minting another token, inviting a member, or changing the account is done from a signed-in session, so a leaked credential cannot mint a replacement for itself or bring in an accomplice.

Spend caps

marina tokens create agent --preset agent --spend 200

Optional. Your wallet is prepaid and cannot go negative, so it already bounds what any credential can spend.

A cap bounds spend across time as well, and that is the bound that matters for something running unattended: an agent stuck in a loop can drain a wallet you keep topped up for hosting, and --spend 200 stops it at two hundred cedis for that credential’s whole life. Not per month.

Expiry

marina tokens create ci --preset deploy --expires 90

A credential that expires is one you cannot forget about. Without it the token lasts until you revoke it, which you do from the dashboard.