Skip to content

For coding agents

Use the repository SKILL.md as the execution policy and these docs as the interface reference.

Connect from the homepage prompt

When a user starts with the prompt copied from opencloud.ai, begin with the product conversation. Ask what they want to build, who it is for, and which single workflow matters most before choosing infrastructure.

Inspect the execution surface first. If a terminal is available, use the CLI even when OpenCloud MCP tools are also present:

  1. Install the pinned release with the checksum-verifying script in the CLI reference.
  2. Confirm opencloud --cli-version is 3.10.3.
  3. When OpenCloud explicitly supplies an exact-app owner Agent session and assigned app ID, run opencloud doctor and opencloud app get "$APP_ID". Use that session directly; do not run account login, create another app, or connect another workspace.
  4. Otherwise run opencloud auth status and opencloud doctor. Reuse a healthy stored account login. If none exists, run opencloud login. The command prints and opens a short-lived HTTPS approval page. The user signs in with a one-time email link or configured password and explicitly approves the CLI; no localhost callback or pasted code is involved.
  5. For that ordinary account flow, run opencloud app list and opencloud app get "$APP_ID". Create a new private app with opencloud app create only when the requested app does not already exist. From the app's source directory, run opencloud app connect "$APP_ID". This creates the non-secret .opencloud/app.json binding and a protected, app-scoped credential.
  6. Use the CLI for the complete draft, isolated-development, deployment, verification, and operation workflow.

The account login is reusable by later Codex or Claude Code sessions running as the same OS user. Its 15-minute access token and rotating 30-day refresh token are stored in the OS keyring under ai.opencloud.cli. A headless host without a usable keyring falls back to a mode-0600 per-user credential file under:

  • $XDG_CONFIG_HOME/opencloud/credentials (normally ~/.config/opencloud/credentials) on Linux;
  • ~/Library/Application Support/OpenCloud/credentials on macOS; or
  • %APPDATA%\OpenCloud\credentials on Windows.

The account token can list, read, and create apps and connect a workspace, but cannot deploy, configure, inspect secrets, or perform owner-only actions. app connect issues a separate 24-hour app credential and the CLI renews it when necessary. opencloud logout revokes the whole login family and every workspace credential issued from it. Never read or print either protected credential backend.

An exact-app owner Agent session is different from both credentials above. It is the AI SDK Codex harness v3 delegation for one assigned app and carries the same app authority as its human owner. OpenCloud exposes its protected path as OPENCLOUD_SESSION_FILE; never inspect or print that file. Through the public CLI, the Agent may perform every operation owned by that app—including deploy, production data and File mutations, rollback, backup restore, secret and access changes, archive, and deletion—without an additional confirmation solely because the operation is destructive or affects production. The Agent must still honor an explicit constraint such as “do not deploy,” use normal validation and idempotency, and verify the result.

The exact-app session cannot create or operate another app, change account settings, authorize or reveal account-owned provider credentials, administer the platform, or access the host or shared infrastructure. Account connection and consent remain human account actions; the Agent may bind or unbind an already available app-account integration only through the typed app-scoped contract. Do not infer exact-app owner authority from merely running as an Agent. AI SDK harness v1/v2 and ordinary human-directed CLI sessions retain their existing, narrower authority and approval behavior.

The CLI reference records the complete owner command tree in the pinned public CLI v3.10.3. Verify the installed version and command help before use. Do not invent a missing command or bypass the public CLI with a raw request; report an absent surface as a product limitation.

CLI success is a stable machine-readable JSON document. In the owner-parity release, failures are also structured, redacted JSON with a non-zero exit status. Parse fields instead of scraping prose. Give every mutation one stable idempotency key and reuse it for the same intended effect after an ambiguous response. An exact-app owner Agent must pass it through every mutating command's --idempotency-key option; a missing key is rejected before a journal entry or request is created. An artifact digest may identify input but does not identify the complete intent. Never change keys or delete journal state merely to bypass an ambiguous outcome or authority mismatch; inspect the exact operation and current app state first. Operation-starting commands follow with bounded waits by default; use --no-follow only when deliberately detaching and retain the returned operation ID. Reconcile without duplicating the mutation:

bash
opencloud operation get "$OPERATION_ID" --follow
opencloud operation list "$APP_ID"

A local timeout does not prove failure. Read the durable operation again. Terminal failure or cancellation exits non-zero with typed diagnostics; after repairing materially different input, use a new idempotency key. Successful app deletion returns terminal evidence before credential revocation and leaves only its exact bounded deletion receipt readable.

Use the hosted MCP workflow only when the surface has no usable terminal or direct API path:

  1. Call get_opencloud_session.
  2. If it reports disconnected, call connect_opencloud for a returning user or continue with start_onboarding for first registration.
  3. For first registration, ask for the user's email only if it is not already available, then call start_onboarding. Do not ask them to invent or check an app domain.
  4. A new email receives awaiting_email_verification with a retained request and no app or credential. The owner confirms their email within 24 hours, then can build personally. Creating a company is optional in Preferences. An account can belong to at most one company; an email domain alone never grants membership.
  5. Give the user the returned launchUrl as the primary owner link. It waits for both email confirmation and deployment, contains no confirmation secret, and then opens the project in the signed-in browser.
  6. Existing emails also receive a project-aware confirmation request. After confirmation, call complete_onboarding with the same onboarding ID. Wait for the owner while confirmation is pending; do not create a replacement request. Existing valid provisional grants remain usable when confirmation delivery is delayed.
  7. Resolve the assigned app from onboarding or get_app, then call get_app_starter with all required capability IDs. Treat its resolved checklist as required build input.
  8. Use create_draft, inspect files, apply_file_changes, and validate_draft; then run isolated dev, exact-revision verification, and promote_dev_revision. If the owner asks to review without deploying, give them the dev session's browserPreviewUrl. It requires an owner or builder browser login and opens a clearly marked Not live preview window with Full size, Tablet, Mobile, and Reload tools around isolated synthetic-user-A state; do not use the raw previewUrl as the human entry link. An explicit no-deploy request stops at this review point and does not authorize promote_dev_revision.

No-terminal builders use https://mcp.<domain>/build; the host root https://mcp.<domain> remains the full lifecycle connector surface for ChatGPT, Claude, and other supported clients without a usable terminal or direct API path. /mcp and /mcp/build remain compatibility aliases for existing clients. MCP is stateful Streamable HTTP: reuse its opaque session ID, keep authorization server-side, and treat draft revisions, file hashes, receipts, and operation IDs as concurrency/evidence contracts. See ChatGPT, Claude, and MCP for the complete state and recovery model.

This MCP path needs no shell, local filesystem, Node.js installation, or CLI. A prompt cannot install a connector. If the tools are absent, state that the OpenCloud connector must be enabled on a supported client surface. Claude Code and Cowork can install the plugin for its workflow skill, but sessions with a usable terminal follow the CLI path above. Claude.ai, Claude Desktop, and Claude mobile can use the connector. Plugins are not currently available directly in ChatGPT mobile chats; a mobile user can steer a configured desktop task through ChatGPT Remote.

If neither the verified CLI nor hosted MCP is available, remain offline and do not claim registration, preview, deployment, or verification.

For MCP first registration, an unverified provisional grant pauses its apps after 24 hours without deleting data. The project-aware confirmation creates the browser session and returns to the non-secret owner launch page.

Never ask the user for an account password, session cookie, OAuth access token, email confirmation link, bootstrap token, service-role key, database credentials, secret values, or a copied connection pack. MCP retains provisional onboarding material server-side. The CLI retains credentials only in its protected per-user store.

Skill representations

Use https://opencloud.ai/skill when a client may require browser-readable HTML. It serves HTML by default and returns the exact Markdown source when the request explicitly accepts text/markdown. The stable https://opencloud.ai/SKILL.md endpoint and the MCP skill resource remain raw Markdown.

Reading order

  1. Read the hosted OpenCloud build skill completely.
  2. Read the quickstart.
  3. Load the control-plane OpenAPI for agent, CLI, deployment, and operations work. Load the separate app runtime OpenAPI for code running inside an app.
  4. Read only the capability guides required by the requested product.
  5. Read the relevant JavaScript namespace pages before writing frontend calls.
  6. Read the manifest, SQL, and verification references before deployment.
  7. For URL aliases, optional route parameters, or query-driven Functions, read App routes. For an owner-provided website hostname, read Custom domains.
  8. Keep the development loop, verification contract, and Functions/server library guide open while implementing.

First-turn checkpoint

Create a real opencloud.yaml and non-empty frontend in the first coherent file batch. Before selecting capabilities, make one monitoring decision: identify any important product failure that the platform's built-in app, deployment, operation, cron, background-job, HTTP-error-rate, and telemetry-freshness signals cannot express. Include telemetry only when a custom product signal is needed; this is an agent decision, not a user approval step. With MCP, call get_app_starter with every capability implied by the product (auth, data, files, functions, jobs, ai, email, notifications, realtime, telemetry, cron, and/or integrations). Implement every returned resolvedCapabilities and capabilityChecklist entry. Inspect the current draft, then use apply_file_changes and validate_draft; with the CLI, run the artifact checker and validate before polishing the UI. Once the first coherent artifact validates, start a development session. Sync each coherent change, inspect the stable preview, invoke Functions only explicitly, and obtain a receipt before promotion. Read Develop safely before production.

Contract rules

  • Author new server-side/MCP app manifests with schemaVersion: 3, no top-level release version, and runtime.sdk.version: 2.2.0. OpenCloud assigns the release name during promotion and stores it on the deployment row. The pinned CLI 3.10.3 defaults new versionless sources to schema 3. Use --version only for a deliberate legacy schema-2 source, and leave an existing schema-2 compatibility version stable across updates. Declare optional files.access, Function access: user | public | system, and a secrets map whose values are generated, required, or optional; declare app-owned email aliases only under email.addresses.
  • For every brokered integration, creating or authorizing an account connection and completing provider consent remain human account actions. With account: app, an exact-app owner Agent may list the app's integration declarations and current bindings plus bounded eligible-connection metadata and opaque IDs for the exact app's declared slots, then bind, configure, or unbind an already authorized connection through the owner-parity integration commands. This app-context projection includes only the delegated owner's connections usable by declared account: app slots. It cannot enumerate the general account catalog, manage connections, expose raw credentials, reveal undeclared providers, include calling_user connections, or cross into another owner's connections. A calling_user binding always remains that calling user's action and authority.
  • For Google Workspace, declare a named google-calendar, google-drive, google-sheets, google-docs, or google-slides slot with account: app | calling_user, cardinality: one | many, and only the provider-scoped read/write capabilities needed. Functions use the typed integrations.calendar/drive/sheets/docs/slides(slot) clients. Account connection and resource authorization are human actions; app-account binding follows the rule above. Never request OAuth tokens or raw Google API access. Discover exact bounded methods from the @opencloud/server declarations or the brokered integrations guide, never by guessing a raw REST or batchUpdate escape hatch.
  • For Google reporting, declare google-analytics, google-search-console, or google-ads with account: app and only its single read capability. The owner binds one exact property, site, or non-manager Ads customer. Functions use the provider-independent integrations.use(slot).call(operation, input) surface; raw requests, GAQL, provider tokens, and unbound resource IDs are not public APIs. The broker owns operation validation so future providers do not require a core SDK release.
  • For Asana, declare provider: asana with only the required asana.* task, assignee, section, custom-field, attachment, story/comment, and event capabilities. Every slot binds one exact human-selected project. Functions use integrations.asana(slot); never accept a raw Asana request, OAuth token, a connection ID outside the exact-app projection, or an out-of-project resource. Sections are list headings or board columns. Task mutations returning current task state also require asana.tasks.read; assignee, section, and custom-field details stay gated by their own read capabilities. Incoming events require account: app, asana.events.receive, asana.tasks.read, and events.function targeting a declared system Function. Handle OpenCloudAsanaEvent.id idempotently because an app's own writes may be delivered back to it. Development uses deterministic synthetic data and contacts no Asana account.
  • For HubSpot CRM, declare provider: hubspot-crm, account: app, and only the needed crm.contacts.*, crm.companies.*, crm.deals.*, crm.owners.read, crm.pipelines.read, crm.notes.write, and crm.associations.write capabilities. Functions use integrations.crm(slot) typed methods. A human authorizes the reusable portal; app binding follows the rule above. Never request a HubSpot token, issue raw provider requests, add delete operations, or invent CRM webhook events. Development uses deterministic synthetic CRM data and does not contact HubSpot.
  • For open banking, declare gocardless-bank-account-data with only the needed bank.accounts.read, bank.balances.read, and/or bank.transactions.read capabilities. Functions use integrations.bankAccountData(slot).bindings/account/balances/listTransactions. Bank consent and exact-account authorization are human actions; app binding follows the rule above. Never request bank login details, GoCardless credentials, requisition IDs, raw account IDs, or raw provider requests. Development uses synthetic data and contacts no bank.
  • For Wise transactions, declare the technical provider wise-balance-webhook with account: app and only payments.received.reconcile. Functions use integrations.receivedPayments(slot).bindings/reconcile. A human creates exactly one Account deposit events webhook in Wise using the notification URL shown under Account Integrations and tests it; app binding follows the rule above. Do not ask them to create Transfer update or Transfer issue events. Never ask for a Wise token, certificate, login, full balance, raw transfer ID, or raw transaction feed; treat ambiguous as manual review.
  • For Slack, declare provider: slack, account: app, and only slack.messages.send and/or slack.messages.receive. Receiving also declares events.message.function targeting a system Function. Functions use integrations.slack(slot).bindings/sendMessage; inbound handlers receive OpenCloudSlackMessageEvent. A human connects the reusable workspace and invites @OpenCloud to an exact channel. App binding selects that channel and mention or all_messages under the rule above. Inbound delivery is at least once; use the stable event id to make side effects idempotent. Never request a Slack token or a connection ID outside the exact-app projection, choose a channel in code, accept slash commands, or assume a Slack tag authorizes a sensitive app action.
  • For Telegram, declare provider: telegram, account: app, and only telegram.messages.send and/or telegram.messages.receive. Receiving also declares events.message.function targeting a system Function. Functions use integrations.telegram(slot).bindings/sendMessage; inbound handlers receive OpenCloudTelegramMessageEvent. A human adds an exact private chat or group through the app's one-time link. App binding selects an owned paired chat and directed or all_messages under the rule above. cardinality: one selects one chat at a time; many can bind several. Private chats and directed groups do not require bot administrator access; all-message groups grant the bot minimal administrator access in that exact group while privacy mode remains enabled. Inbound delivery is at least once, so make side effects idempotent with the stable event id. Never request a bot token or raw chat ID, handle raw Telegram updates, choose a chat in code, disable privacy mode, or treat a Telegram sender, mention, command, or reply as authorization for a sensitive action.
  • Import opencloud from /_opencloud/sdk.js. Do not fetch runtime config, construct a client, import an immutable SDK URL, or handle credentials.
  • Use data.table(...), managed files, functions.call/stream, notifications.status/subscribe/unsubscribe, and realtime.subscribe/publish. Do not issue raw REST, Storage, Function, Web Push, or Realtime protocol requests.
  • Every app table declares id as its primary key. Browser Files uses upload({ data, ... }), replace(file, { data, ... }), and flattened attach({ data, table, ... }); retry keys and timeouts are private.
  • Prefer files.attach when a file and its database metadata must be created together. It handles ambiguous writes and cleanup; store its opaque file ID, never a bucket or object path.
  • Implement Functions only as export default defineFunction({ input: schema.*, handler }); input is already parsed. Function names and matching entrypoint directories use lowercase kebab-case. Use named errors.* factories and ai.generateText, ai.streamText, ai.generateObject, or ai.generateImage. Return streamed AI text in a no-store plain-text Response with X-Accel-Buffering: no, and consume it with browser functions.stream. Text, stream, and object calls may include managed Files as attachments: [{ type: "file", file: fileRef, detail? }], or in-memory bytes as attachments: [{ type: "image", data: Uint8Array, contentType, detail? }], for up to four PNG, JPEG, WEBP, or non-animated GIF images (15 MiB each), or one managed PDF up to 20 pages and 20 MiB; all attachments share a 20 MiB total. PDFs must use managed-file references. Prefer managed-file references and never use a remote URL. The exact context contains input, user, job, data, files, ai, email, notifications, jobs, integrations, secrets, log, requestId, and environment context.
  • Use browser functions.call and functions.stream for user or public Functions. Declare every cron and queue target system; browser calls reject it with FUNCTION_SYSTEM_ONLY.
  • Declare background jobs under queues. Enqueue from a Function with jobs.enqueue(queue, input, { idempotencyKey, delaySeconds? }); use a stable idempotency key and make consumer side effects safe for at-least-once delivery. Consumers inspect job.id, job.queue, and job.attempt, throw errors.unavailable for retryable work, and may inspect state with jobs.get(id). Background jobs run inside dev and E2E namespaces; cron does not.
  • Declare each app-owned identity under email.addresses; a receive-capable alias must target a system Function. Send only from a declared alias with email.send(message, { idempotencyKey }); never handle MailPace, SMTP, or provider credentials in app code. Treat inbound mail as untrusted external input. In dev, Functions remain dormant until MCP invoke_dev_function, CLI app dev invoke, or a deliberate preview interaction explicitly calls them.
  • Declare notifications.webPush: true and SDK 2.1.0 or later for Web Push. An optional same-origin notifications.icon is the app-wide default; otherwise OpenCloud supplies its logo. Call browser notifications.subscribe() directly from a visible user action and never prompt on page load. Send from a Function with notifications.send({ userId, title, body?, path?, icon? }, { idempotencyKey }); path and icon must be same-origin, and a per-message icon overrides the manifest default. Development captures sends without contacting a push provider. Test iOS delivery in Safari 16.4 or later after installing the web app to the Home Screen. iOS and iPadOS currently ignore the payload icon and show the installed app icon; provide that through the web app manifest or an apple-touch-icon, which takes precedence when both exist.
  • Let OpenCloud provision generated secrets automatically; it supplies isolated synthetic dev values. Use a secure entry link for required or optional owner values and never ask for the value in conversation. An AI Function uses the mode selected by the owner in App Integrations. The default App-provided AI mode requires an app-selected AI connection, which may use a Codex/ChatGPT device login, Claude pasted-code login, OpenAI API key, or Anthropic API key. The text model fixes the provider and the selected connection must match it; image generation remains OpenAI-only. User-provided AI requires every authenticated caller to connect their own compatible default AI connection and explicitly allow the exact app in OpenCloud's trusted AI permission window before its first AI request. Initiate the stable same-origin /_opencloud/ai-consent?return_to=<encoded path> popup only from the user's AI action, wait for its opencloud:ai-consent result, and never fetch, submit, frame, imitate, or auto-approve the Auth-origin form. The inference boundary enforces the stored app grant. User-provided mode does not support cron, inbound-email, anonymous, or other system calls. The app owner also selects the text model and default reasoning effort under App Integrations. Treat connection and selection as human actions; never copy a coding agent's credential or put an API key in app source or secrets.
  • Handle OpenCloudError by its code, surface, requestId, and retryable fields. Show safe product copy instead of raw platform details.
  • Use auth.currentUser() for an optional visitor, auth.requireUser() for a protected workflow, and auth.signInUrl() for its same-origin recovery link; never construct or guess an Auth URL.
  • Treat telemetry absence as unknown or quiet, not healthy.
  • Declare important app-owned custom metrics and their fixed thresholds together under observability.metrics and observability.alertRules. Manifest rules are validated before promotion and activate atomically with the deployment; do not require a post-deployment rule-creation step for intended app behavior. Use alert-rule put only for an immediate operational rule or override. If it becomes durable product intent, move it into the next manifest and remove the override.
  • Keep credentials and brokered tokens out of files, output, and reports.
  • For an exact-app owner Agent, treat its assigned app—not operation type—as the authorization boundary. Do not add a per-operation approval for production or destructive work. For ordinary human-directed sessions and legacy or narrower Agent credentials, preserve the existing requirement for clear user authorization before destructive actions.
  • Never invent or rotate a schema-3 release version in source. Read the publisher-assigned version from deployment results.
  • Preserve exact operation, deployment, artifact, cron, and backup evidence.
  • Replace the fail-closed starter tests/opencloud.e2e.js with REQ-tagged external tests for the requested primary outcomes. Drive visible controls, assert exact durable state with the distinct marker supplied to each test, use uniqueValue(prefix, maximumLength) for bounded fields, and clean fixtures through the UI. In dev, each test starts with a fresh schema, managed Files namespace, Function namespace, synthetic users, and browser contexts; five run in parallel by default. Scope duplicate labels through their dialog/card, use clickIfVisible(locator) for genuinely optional cleanup controls, close overlays in finally, and wait for cleanup state to converge. Textual locator queries may use bounded regular expressions, and selectOption accepts a raw value or bounded { value }, { label }, and { index } selectors.
  • Make at most ten verify_dev_session attempts for one build. Diagnose with validation, preview reads, and Function evidence between attempts; stop with the exact structured failure if the tenth attempt still fails.
  • Use managed opencloud.files methods and opaque file IDs; never construct raw Storage paths, buckets, object names, or authorization headers.

Common task recipes

js
import { opencloud } from "/_opencloud/sdk.js";

await opencloud.auth.requireUser();
const tasks = opencloud.data.table("tasks");
const rows = await tasks.list({
  select: ["id", "title", "done"],
  where: { done: false },
  orderBy: { column: "created_at", direction: "desc" },
});
const created = await tasks.create({ title: "Ship the app" });
await tasks.updateById(created.id, { done: true });

const { file, record } = await opencloud.files.attach({
  data: selectedFile,
  name: selectedFile.name,
  table: "task_files",
  values: { task_id: created.id },
});

const summary = await opencloud.functions.call("summarize", {
  taskId: created.id,
  fileId: file.id,
});

const unsubscribe = await opencloud.realtime.subscribe(
  "tasks",
  ({ event }) => event === "changed" && void refreshTasks(),
);
await opencloud.realtime.publish("tasks", "changed", { id: created.id });

Keep the unsubscribe function for teardown. Use files.upload when only the managed file is needed; use files.attach when the app also needs an RLS row.

Definition of done

An online app is done only when:

  • product checks and authoritative OpenCloud source validation pass;
  • the durable deployment operation succeeds;
  • the isolated dev preview passes and a receipt covers the exact promoted source and migrations;
  • the authoritative MCP verify_app or CLI app verify operation passes;
  • the feature-aware verifier passes where configured;
  • the monitoring decision was made and every required app-owned custom failure signal connects its metric, manifest rule, and real browser failure boundary;
  • logs and telemetry were inspected honestly; and
  • the canonical HTTPS app remains active, unless intentional app deletion completed with its exact terminal receipt; and
  • MCP promote_dev_revision or CLI app dev promote reported the live URL and the dev session stopped only after production verification.

If online credentials are absent, produce an offline-valid deterministic bundle and list the exact remaining live steps.

Self-hosted infrastructure for agent-built applications.