Repository navigation
docs(deployment): Cloud Connection operator page — connect a self-hosted server to ObjectStack Cloud - #24147
Conversation
…rver A new page under content/docs/deployment/ walks a self-hosted operator through connecting their server to an ObjectStack Cloud account from Setup -> Apps -> Cloud Connection: what the connection gives, the prerequisites, the connect steps and the panel's states, what the server stores, rename and disconnect. Every server-side statement is read from packages/cloud-connection (and the pinned objectui panel) or measured on a showcase dev boot against a stand-in control plane. Stable anchors: #connect, #rename, #disconnect. One-line cross-links from the three pages that already name @objectstack/cloud-connection. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
check:role-word (ADR-0090 D3) flagged one new use; the sentence now says which cloud accounts may approve, and with what permissions. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…oud-connection-guide
….json Generated by `node scripts/docs-audit/check-audit-scope.mjs --write`; the one added line is the new content/docs/deployment/cloud-connection.mdx, which check:docs-audit-scope requires for every hand-written page. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
…ontract The session-only check on connect/disconnect is now a known-defect note pointing at its card, with interim advice for operators; the status row no longer lists the approver's email and organization as fields anonymous callers read, and points at the same card; the OS_RUNTIME_VERSION fallback is named a known defect with its card. #disconnect unchanged. Claude-Session: https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
席位核对 · ACCEPT · PR #24147 (draft, head
|
Fixes #24066
Clause-②: no
Body refreshed by the
domain:devxseat 2 at 2026-10-17T05:06Z, after patch round 1.A new operator page, Cloud Connection — connect a self-hosted server to ObjectStack Cloud, under
content/docs/deployment/, plus one-line cross-links from the three pages that already name@objectstack/cloud-connection, and the page's generated line inscripts/docs-audit/handwritten-docs.json. Docs only; no product code.Published path and anchors
/docs/deployment/cloud-connection(filecontent/docs/deployment/cloud-connection.mdx, listed incontent/docs/deployment/meta.jsonafterinstall-modes).[#id]suffix, so a heading rewording cannot move them):#connect,#rename,#disconnect; also#what-you-get,#before-you-start,#what-the-server-stores,#reference.@mdx-js/mdx3.1.1 +remark-gfm+ fumadocs-core's ownremarkHeading(the slug authority the site uses), and bypnpm check:doc-anchors(exit 0).Each section and what it rests on
Code reads are at
origin/main9ffe8af (this branch changes no code). The Setup panel was read at the pinned.objectui-shaf8267df93aba. The branch later mergedmainwith the pin at 5e15928b4631, and between the two pinspackages/app-shell/src/console/cloud-connection/has no diff and the encloudConnectionstrings are identical.packages/cloud-connection/src/cloud-connection-plugin.tsheader andbind/start,bind/poll;README.md"Boundary"bind/pollstoresruntimeId,organizationId,accountEmail,boundAtfrom the cloud's answer;GET /org-packagesforwards with the stored bearer (empty,connected:false, unbound);marketplace-install-local-plugin.tssendsthis.credentials.read()?.runtimeTokenon the cloud manifest fetch; README table rows "Unbound (anonymous)"packages/cli/src/commands/serve.tsstep 5 andServe.planMarketplaceWiring(cloud arm only whenresolveCloudUrl()is truthy, skipped when the host composesCLOUD_CONNECTION_IDENTITIES, skipped in runtime host-kernel mode);cloud-url.tsDEFAULT_CLOUD_URLand the four decline spellings. Measured:OS_CLOUD_URL=offboot answersGET /api/v1/cloud-connection/status404ENDPOINT_NOT_FOUND, Setup Apps group has no Cloud Connection entry, page read 404fetchincloud-connection-plugin.ts; the panel only calls same-origin/api/v1/cloud-connection/*(CloudConnectionPanel.tsxBASE)connection-credential-store.tsDEFAULT_CONNECTION_CREDENTIAL_PATHresolved againstprocess.cwd();docker/DockerfileWORKDIR /srv/apprequiredPermissions: ['setup.access'](platform-objects/src/apps/setup.app.ts);setup.accessinADMIN_FULL_ACCESS_CAPABILITIES(spec/src/identity/eval-user.zod.ts) andorganization_admin(plugin-security/src/objects/default-permission-sets.ts); routes checksession.userIdonly (resolveSession), held sessions refused 403 (refuseHeldSession). Measured (below)cloud-connection-ui.ts(label: 'Cloud Connection', header subtitle,group: 'group_apps'); panel states inCloudConnectionPanel.tsx; strings from objectuipackages/i18n/src/locales/en.tscloudConnection.*. Measured: merged Setup nav Apps group = Packages, Packaged Automation, Browse Marketplace, Installed Apps, Cloud Connectionbind/start:client_iddefaultobjectstack-cli, scopeopenid profile email,interval ?? 5,expires_in ?? 600,withContextaddsruntime_nameandruntime_version. Measured on the wirepollintervalMath.max(code.interval, 2);bind/pollpending onauthorization_pendingandslow_down; runtime token persisted then deleted from the answertranslateFailureCode(expired_token,access_denied), client-side expiry,getJsonmessage precedence; server messages frombind/startandbind/poll. Measured: denial answers 400DEVICE_CODE_FAILEDwithdeclaredCode: access_denied; cloud down answers 502DEVICE_CODE_FAILEDfetch failedstopPolling(); refreshStatus()only; the connection completes only insidebind/pollConnectionCredentialStore.write(mode0o600) and the fieldsbind/pollwrites;credential()reads the store per request. Measured file mode 600 and fieldsname: hostname()in the bind body andruntime_namein the link; no rename route exists among the mounted/api/v1/cloud-connection/*routes; storedruntime_idre-sent on bind;runtime_version=OS_RUNTIME_VERSION ?? this.versionwithversion = '0.3.0'. MeasuredPOST /unbind: revoke first (best effort), then rewrite to{ runtimeToken: '', runtimeId }orstore.clear(); paneldisconnectignoresrevoked. Measured both cloud-up and cloud-downstatusanswers without a session and passes the cloud'sdata.connectionthrough;credential()prefersOS_CLOUD_API_KEYMeasurement
Showcase dev boot (
OS_CLOUD_URL=http://127.0.0.1:39871 pnpm dev -- --fresh -p 38517, workspace built at 9ffe8af) against a throwaway stand-in control plane in the session scratchpad that answers the five cloud endpoints the plugin calls and logs every request it receives. Readings:status200bound:false;bind/start401UNAUTHENTICATED"Sign in to this environment to connect a cloud account.";unbind401 "Sign in to this environment."bind/start200:user_code,interval: 5,verification_uricarryingruntime_name=vm&runtime_version=0.3.0(this boot's/api/v1/discoveryversion reads 18.0.0-next.2).client_id objectstack-cli,scope openid profile email; bind bodyname vm,runtime_version 0.3.0,scope, token; a re-bind addsruntime_id; status and revoke carryBearer oscc_(prefix only logged).bind/pollpending 200{pending:true}; after approval 200 withruntime_idandconnection, and noruntime_tokenin the answer; credential file mode 600 withruntimeToken,runtimeId,controlPlaneUrl,organizationId,accountEmail,boundAt.unbind(cloud up){revoked:true, cleared:true}; file becomes{runtimeToken:'', runtimeId}; statusbound:falsewithruntimeIdkept.bound:true,connection:null;unbind{revoked:false, cleared:true}.GET /api/v1/meta/app/setup403PERMISSION_DENIED) received 200 fromunbindand frombind/start. The page names this as a known defect and links cloud-connection: bind/start, bind/poll and unbind admit any signed-in user (no Setup Access check), and GET /status serves the approving account's email and organization id to anonymous callers #24148, which the seat filed from this run, together with the anonymousstatusread of the connection.Both dev servers, the stand-in and the run's
examples/app-showcase/.objectstack/directory were removed after the run.Cloud-side statements left unverified
objectstack-ai/cloud is not readable in this session, so the page describes the cloud account only generically ("open the link, sign in to the cloud account, approve the request whose code matches"). Not verified, for the cloud seat:
runtime_idupdates the stored name.runtime_idreattaches to the same registration (the page says the server sends it "so the cloud can" do so).revokedoes cloud-side, and how an operator removes a connection from the cloud account afterrevoked: false.oscc_bearer on the org catalog and manifest routes.intervalandexpires_invalues (the page states only the server's fallbacks of 5 s and 600 s).Patch round 1
The seat widened the file surface (claim amendment
6229839180on #24066), and the dev made these changes:node scripts/docs-audit/check-audit-scope.mjs --writeadded the one generated line,content/docs/deployment/cloud-connection.mdx, toscripts/docs-audit/handwritten-docs.json.pnpm check:docs-audit-scopenow exits 0.#before-you-startnames the sign-in-only check as a known defect (cloud-connection: bind/start, bind/poll and unbind admit any signed-in user (no Setup Access check), and GET /status serves the approving account's email and organization id to anonymous callers #24148), with interim advice.#referencestatusrow no longer lists the approving account's email and organization; it points to cloud-connection: bind/start, bind/poll and unbind admit any signed-in user (no Setup Access check), and GET /status serves the approving account's email and organization id to anonymous callers #24148.#renamenames theOS_RUNTIME_VERSIONfallback as a known defect (cloud-connection: the server reports the plugin's hardcoded 0.3.0 as its runtime_version to the cloud (bind/start verification URI and bind body) when OS_RUNTIME_VERSION is unset #24150).revoked: false.#disconnectis unchanged.Gates
Derived by
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; 41 commands) and run on the final head a6e9135 (6 paths, 52 commands): all 52 exit 0,check:docs-audit-scopeincluded. Round 0 (head 4d2f5c6, 41 commands) hadcheck:docs-audit-scopeat exit 1, which patch round 1 resolved.check:skill-examplesfirst refused with exit 3, PREREQUISITE NOT MET (no.d.tsfor spec and client-react), and gave exit 0 after those packages were built.--ranreconciliation with exit codes: 52 derived, 52 run, 0 NOT-MEASURED, 0 UNRUN.node scripts/pm/check-governed-merges.mjs --branch claude/issue-24066-cloud-connection-guide: NOT governed, 239 changed lines.Acceptance notes
CloudConnectionPluginConfig.credentialPathis not shared withMarketplaceInstallLocalPlugin, which buildsnew ConnectionCredentialStore()at the default path. A host that moves the credential gets an install-local that reads the old location. Nothing in the repo composescredentialPath. Carrier: none.unbind. Carrier: none.content/docs/deployment/environment-variables.mdxsaysOS_CLI_CLIENT_IDdefaults to nothing and servesos login, while the plugin defaults it toobjectstack-clifor the device flow.OS_RUNTIME_VERSIONis not on that page. Not edited: outside this card's surface. Carrier: none.examples/app-showcase/objectstack.config.tscomment "the CLI no longer auto-injects these" contradictsserve.tsstep 5, which auto-wires them again. Carrier: none.slow_downis read as pending, but the panel keeps its interval (RFC 8628 says to add 5 s). Not measured against a cloud that sends it. Carrier: none.Generated by Claude Code