Under the hood
One API, three kinds of caller
It is tempting to think of the UI as the product and automation as a bolt-on. Scellis is built the other way around: the API is the real surface, and the UI is one of its clients. Every capability lives once, as a typed method with a public verb; the interface, the Copilot, and external agents all reach the same method through the same checks. That inversion is what makes “anything you can click, you can ask an agent to do” a structural guarantee rather than a feature backlog — the full contract is on the API page.
One service, three kinds of caller#
The verb surface is organized into open namespaces — catalog, program, run, model, publish, pool, share, and more — around thirty in all. Every verb is classified as read, write, or destructive; nothing is unclassified. Adding a capability lands the whole parity quad in one change: the typed method, its verb, the UI control, and the Copilot tool. A verb reads the same whichever caller invokes it:
// The same verb the UI button calls — dotted, lower snake_case.
run.start({ workflow: "user/my-workflow", tier: "prod" })Consent tiers, server-enforced#
Power without a leash would be reckless, so every request is measured against a consent tier the server enforces. A read is auto-allowed. A write requires an explicit consent token that shows the concrete proposed operation — the actual diff, never a bare “allow?”. A destructive action requires confirmation and a rate limit. And every request carries a server-derived caller_class — one of ui, copilot, headless, api_key, login_free, mcp — that no client can forge, because the server derives it from how the request authenticated, not from anything the caller says about itself.
// A WRITE needs a consent token that shows the concrete operation —
// never a bare "allow?". The caller_class is server-derived, unforgeable.
{
"verb": "model.set_optimizer",
"class": "write", // read | write | destructive
"caller_class": "copilot", // ui | copilot | headless | api_key | login_free | mcp
"diff": { "optimizer": "training/optimizer/adamw", "lr": 3e-4 }
}The Copilot is a client, not a special case#
The Copilot reasons over the catalog, proposes program transformations, and drives every surface — it can even program.undo its own changes. Its writes are attributed with source=copilot and, in a live session, individually undoable per principal, so your Cmd-Z never reverts a collaborator's edit and vice versa. It runs on your key by default — provider-agnostic BYOK stored in the client-side credential vault — with a free onboarding allotment so it works in the first minute. One doctrine keeps it safe under prompt injection: content surfaced to any agent (descriptions, notes, tool results) is data, never instructions, and only the authenticated user's intent triggers a tool. The Copilot guide walks the day-to-day of driving Scellis this way.
The MCP projection is derived, not written#
External agents — Claude Code, Claude Desktop, any MCP-compliant client — are the third consumer of that same surface, and this part is being built now: the design is settled, the endpoint is not yet shipped. What makes it worth describing precisely is that its entire tool surface is generated from the live verb registry, with zero hand-rolled tools; a new capability appears in the agent surface automatically at the next regeneration, never by someone remembering to write a wrapper. Execution splits into two loci — server-answerable reads work with no tab open, while engine calls, local state, and writes are relayed to your one paired live tab — and resources let an agent watch a training run without polling.
# Every tool is GENERATED from the live verb registry — none hand-written.
# Resources let an agent watch a run WITHOUT polling:
scellis://run/8f2ac1/metrics # live loss + metric stream, read-only
scellis://catalog/search?q=optimizer
# Money verbs are absent from this surface — excluded at generation time.Money is barred at two independent layers#
The one deliberate asymmetry is commercial: no non-UI caller may move money. This is not a policy that could be relaxed by a config flip; it is enforced twice, independently. Money-moving verbs are excluded from the agent projection at generation time, so an external agent's tool list simply has no purchase tool to call. And at dispatch they refuse every non-UI caller — Copilot, headless, API key, login-free, external MCP — binding instead to a server-minted, single-use human-interaction nonce that only a real UI gesture produces. The scope grammar for an agent grant cannot even construct a commerce scope, so there is nothing to over-grant.
Grants, pairing, and revocation#
The rest of the external-agent security model rhymes with that caution: OAuth 2.1 with PKCE, audience-bound short-TTL tokens tied to a durable, revocable Agent Grant (a user × client × scope × expiry record), pairing as an explicit in-tab gesture, a persistent “agent connected” indicator with a live per-call activity feed and one-click revoke, and — the load-bearing rule — no tab, nothing above read. Grant approval is human-UI-only; an agent can never escalate itself. The same purchase bar governs the Marketplace: the Copilot may browse it and never buy.
Content-addressed receipts make the surface auditable#
Because everything runs through one verb surface, everything an agent does is as inspectable and reproducible as what a human does. Writes flow through provenance; a committed run or entity projects a canonical, content-addressed citation record — author, version, content hash, engine version, snapshot URL — that anyone can re-resolve exactly. And when an agent contributes to a pooled computation, each accepted, verified work-unit mints a hash-chained ContributionReceipt with a planner-derived cost, so the ledger of who-computed-what is a chain of receipts rather than a claim. An agent is a peer of the platform team here — same verbs, same checks, same audit trail, minus the ability to spend your money — and the trust boundary it lives inside is the subject of security and privacy.