Core concepts
This page explains the six ideas everything else on the platform is built on. One running example threads through it: Priya, an engineer at Acme, ships a support agent with her teammate Sam, wires up automated deploys, and never once files a ticket to understand her own permissions.
Organizations and projects
An organization is the top-level container — usually a company. Acme is an organization.
A project is a private workspace inside an organization. Everything you create lives inside exactly one project: agents, functions, secrets, topics, vector indexes. Each project is isolated from every other one, with its own members, network boundary, and quota. Acme has a project called ml-team, and that's where Priya works.
Every project gets a short id — a six-character code such as ab12cd. It is immutable: once assigned, it never changes. The short id appears in the addresses of everything inside the project, including public URLs like https://my-agent-ab12cd.apps.codyhill.dev. Because renaming a project leaves the short id alone, a rename never breaks a running workload or a bookmarked URL. That's a deliberate promise.
A project also holds one Crusoe Cloud connection: a Crusoe Cloud access key, saved once by a project admin. Everything the platform builds for you becomes a container image, and that image is stored in a repository in your own Crusoe Cloud Registry — your account, your quota, your bill, and no other customer's images beside it. A project with no connection cannot deploy, and the refusal arrives at submit rather than as a failure inside a build. Connect it once and it stays connected: see connect your Crusoe Cloud account.
Roles: the two-axis model
Most clouds have one ladder of permissions. This platform has two separate axes, and understanding them explains every "why can't I see this?" moment:
- Metadata authority — power over the project object: rename it, delete it, manage who's a member.
- Resource authority — power over the project's contents: agents, functions, secrets, data.
The roles, in plain words:
| Role | Metadata authority | Resource authority |
|---|---|---|
| Project member | none | use the project's resources |
| Project admin | manage this project and its members | everything, including delete, reveal, and minting credentials |
| Org admin | admin on every project in their org | admin on every project in their org (inherited, live) |
| Platform admin | everywhere | nowhere |
Read that last row again, because it's the platform's signature idea: the people who operate the platform cannot read your project's contents. A platform admin can see that ml-team exists and can manage its membership. Its agents, secrets, and conversations return "not found" to them.
When a platform admin genuinely needs inside — say, to repair something broken — they must take a break-glass grant. That is a temporary membership they grant themselves, and it comes with four strings attached:
- It requires a written reason.
- It appears in the project's member list, where every member can see it.
- It expires on its own: 4 hours by default, 24 maximum.
- It is recorded in the audit log.
There is no quiet path in. Details in Break-glass and audit.
One behavior surprises people. Call the API for a project you have no grant at all on, and it returns 404 not found — never 403. The platform won't even confirm the project exists. You only see 403 once you're inside a project but lack the role for a specific action.
In our example: Priya is admin on ml-team, Sam is member. Sam can deploy and invoke agents. Priya can additionally add members, delete things, and mint credentials. Dana, a platform admin at Acme's provider, can see the project exists — and nothing inside it.
API keys and service accounts
Signing in gives you a session token that lasts 12 hours — fine for humans, useless for an automated pipeline. For machines there are API keys: long-lived credentials that look like cai_<keyid>_<secret>.
You send a key in the Authorization: Bearer header, or as the CLI's $CAI_TOKEN. "Bearer" is the important word: whoever bears the key can use it, with no further check of who they are. Treat a key exactly like a password.
Two kinds of principal — the identity a request acts as — can hold a key:
- A personal key acts as you. Your permissions are re-checked live on every request, so if you lose a role, your key loses it on its very next call.
- A service account is a machine identity that belongs to one project. It has an email-style name like
ci-deploy@ab12cd.cai.localand a project role of its own. Its authority never extends past its project's contents.
Priya creates a service account ci-deploy with the member role and mints a key for it. Her GitHub Actions workflow — the automation that builds and deploys on every push — exports the key as $CAI_TOKEN and runs platformctl deploy. No human credentials sit in that pipeline, and revoking that one key ends exactly that one access.
Three sharp edges, stated plainly:
The full secret appears in exactly one API response, at creation. Only a hash is stored — a one-way scramble that cannot be turned back into the key. Nobody, including support, can show it to you again. If it's lost, revoke it and mint another.
- A service account cannot create or manage credentials. A leaked pipeline key therefore can't mint replacement keys for itself, which is what makes revoking it meaningful.
- Revocation is immediate: the next request presenting a revoked key is refused.
Manage all of this in the console — see Service accounts and API keys — and see API authentication for the full credential model.
Quotas: the caps on what a project can run
Every project has caps on what it can consume: running instances, services, CPU, and memory. Fresh projects start with defaults (50 running instances, 100 services, 10 CPU / 20Gi reserved, 20 CPU / 40Gi maximum). A cap of 0 means unlimited.
The service cap is not a workload cap, and the difference bites: each workload holds roughly four Service objects at rest, and every retained revision holds one more. A redeploy-heavy afternoon can consume the cap with only a handful of workloads deployed.
Quotas are read-only and live. The console's Quotas page and the quota API show real usage against real limits, straight from enforcement — green under 70%, amber to 90%, red above. Raising a cap is an administrator action, not an API call.
Quotas fail loudly, not mysteriously. When ml-team is at its service limit and Sam tries to deploy one more function, the deploy is refused up front with:
this project is at its service limit (100 / 100): deploying needs at least one
more service and cannot proceed. Delete an agent or function, or ask an admin
to raise the project's service quota, then deploy.
See Quotas and audit and the full limits reference.
Scale-to-zero: idle workloads stop
Workloads on this platform don't idle — they stop. A few minutes after the last request, an agent or function scales down to zero running instances. It costs no capacity while idle, and it doesn't count against the project's running-instances quota while at zero.
The next request cold-starts it: the platform spins an instance back up before answering. That first response is noticeably slower than the ones that follow.
Priya's support agent gets traffic during business hours and sits at zero overnight. Nobody turns anything off; nobody turns anything back on.
Sometimes a cold start is unacceptable — a live demo, or an endpoint where every millisecond counts. For those, set minimum instances to 1 in the workload's configuration. One instance then stays warm, and it occupies quota the whole time. The mechanics live in Autoscaling and scale-to-zero.
Data plane vs. management plane
The platform draws a hard line between two kinds of request.
The management plane (also called the control plane) is everything that creates, changes, or inspects state: deploy, delete, logs, secrets, membership, keys. It always requires a credential.
The data plane is talking to a deployed agent or function — invoke, stream, memorize. It also requires a credential by default: this install ships INVOKE_AUTH_REQUIRED=true, and an anonymous call is refused with a 401 that names the setting.
this platform requires authentication to invoke agents (INVOKE_AUTH_REQUIRED=true).
Sign in (POST /v1/auth/login) and send 'Authorization: Bearer <token>'
That default is a response to a real hole: while the route was open, an anonymous POST to /v1/agents/{name}/invoke returned a complete model turn for an agent the caller had nothing to do with. A platform operator can open the route install-wide, but that opens every tenant's agents at once, which is why it is not how you give your users an unauthenticated path.
The supported way to do that is per-endpoint: publish that one workload through the gateway with an explicit auth mode. Which brings up the second thing "open" could mean, and the platform is deliberately strict about it too: your workload itself is private by default. A newly deployed agent or function is not published. The authenticated management plane is the only route to it, and its https://<name>-<project-short>.apps.codyhill.dev address is reserved but serves nothing.
Publishing is an explicit opt-in, and it will not proceed until you have named two things: who may call it (apikey, jwt or none — there is no default, and a bare true is refused by name) and a rate limit (100/minute). Both are refused if missing, because a public invoke path with no caller budget is an unbounded spend of your inference key. Choosing none with a generous limit is genuinely open: anyone who finds the URL can spend this project's inference budget. See invoke your agent for how to publish one on purpose.
The one thing no configuration opens: writing to an agent's long-term memory always requires authentication, even on an install where invoke has been opened. Memories get read back into other callers' conversations later, so an anonymous write would poison everyone's results. The API says it best:
memorize requires authentication: it writes durable memory that later callers
read back. Invoke without 'memorize', then call
POST /v1/agents/{name}/sessions/{id}/memorize with a session token.
So in our example: Priya publishes the support agent through the gateway with --auth none --rate-limit 100/minute, and Acme's customers chat with it anonymously at that one published address. Every other call — hers, Sam's, and the ci-deploy service account's — carries a credential, on the management plane.
Every management-plane change lands in the project's audit log — an append-only record of who did what and when, readable by every member, newest first. That covers each deploy, each key minted, each member added, and even Dana's hypothetical break-glass grant.
Full details in Invoking agents and Security overview.
The whole model on one card
| Concept | The one thing to remember |
|---|---|
| Organization | Owns projects and users; usually your company. |
| Project | The isolation unit. Everything lives in exactly one. Its short id never changes. |
| Metadata vs. resource authority | Controlling a project's existence and membership is separate from touching its contents. |
| Platform admin | Runs the platform; can see your project exists, cannot see inside it without visible break-glass. |
| 404 rule | No grant on a project means 404, never 403 — existence is not disclosed. |
| Session token | 12 hours, for humans. |
| API key | cai_..., long-lived, shown once, revoked instantly, for machines. |
| Service account | A machine identity locked to one project; can never mint credentials. |
| Quota | Live, visible, and loud when hit; raising it is an admin action. |
| Scale-to-zero | Idle workloads stop; the next request cold-starts them. |
| Data plane | Invoking agents — authenticated by default; publish a gateway endpoint for anonymous callers. |
| Management plane | Changing anything — always authenticated, always audited. |
Next steps
- Quickstart: deploy your first agent — put the concepts to work.
- Projects and access — managing members and invitations in the console.
- Security overview — the trust model in depth.
- Glossary — every term on this page, alphabetized.