{"openapi":"3.1.0","info":{"title":"Nerdit Cloud (web)","version":"0.1.0"},"paths":{"/api/health":{"get":{"tags":["health"],"summary":"Health","description":"Report process liveness without depending on external services.","operationId":"health_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"anyOf":[{"type":"string"},{"additionalProperties":{"type":"string"},"type":"object"}]},"type":"object","title":"Response Health Api Health Get"}}}}}}},"/api/ready":{"get":{"tags":["health"],"summary":"Ready","description":"Report role-specific dependency readiness.","operationId":"ready_api_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"integer"},{"type":"string"}]},"type":"object","title":"Response Ready Api Ready Get"}}}}}}},"/api/auth":{"get":{"tags":["auth"],"summary":"Auth Boundary","operationId":"auth_boundary_api_auth_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"501":{"description":"Not Implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/link":{"get":{"tags":["link"],"summary":"Link Boundary","operationId":"link_boundary_api_link_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"501":{"description":"Not Implemented","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/auth/login":{"get":{"tags":["auth"],"summary":"Login","operationId":"login_api_auth_login_get","parameters":[{"name":"return_to","in":"query","required":false,"schema":{"type":"string","default":"/","title":"Return To"}},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Identity"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Request"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/provider-config":{"get":{"tags":["auth"],"summary":"Provider Config","description":"Serve the authorization page the Supabase values a browser may hold.\n\nThree, and deliberately not the fourth: the publishable key is\nbrowser-safe by definition, the project URL and the OAuth client id are\nalready public in the authorization redirect every sign-in follows, and\n``supabase_oauth_client_secret`` is the one value here that would turn\nthis unauthenticated GET into a credential leak. Serving the other\nthree rather than baking them into the bundle keeps the frontend build\nenvironment-free, which is how the rest of the SPA already works.","operationId":"provider_config_api_auth_provider_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderConfigView"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/auth/social":{"get":{"tags":["auth"],"summary":"Social Login","description":"Start one named social handshake.\n\n``provider_name`` is typed as the two-name literal rather than\n``str``, so an unknown provider is refused by validation before any\nURL is built — the console offers the providers it publishes a\nbutton for, not whichever ones the project happens to have on.","operationId":"social_login_api_auth_social_get","parameters":[{"name":"provider","in":"query","required":true,"schema":{"enum":["github","google"],"type":"string","title":"Provider"}},{"name":"return_to","in":"query","required":false,"schema":{"type":"string","default":"/","title":"Return To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Request"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/social/callback":{"get":{"tags":["auth"],"summary":"Social Callback","description":"Redeem one social code and mint this console's own session.\n\n``state`` is required, and GoTrue delivers it by preserving the\nquery on the ``redirect_to`` it was handed. If a provider release\never dropped it instead, every social sign-in would 401 here rather\nthan quietly losing its CSRF check — which is the direction this\nwhole flow is built to fail in.","operationId":"social_callback_api_auth_social_callback_get","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","title":"Code"}},{"name":"state","in":"query","required":true,"schema":{"type":"string","title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/callback":{"get":{"tags":["auth"],"summary":"Callback","operationId":"callback_api_auth_callback_get","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","title":"Code"}},{"name":"state","in":"query","required":true,"schema":{"type":"string","title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/session":{"get":{"tags":["auth"],"summary":"Current Session","operationId":"current_session_api_auth_session_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/auth/logout":{"post":{"tags":["auth"],"summary":"Logout","operationId":"logout_api_auth_logout_post","responses":{"204":{"description":"Successful Response"}}}},"/api/auth/logout-all":{"post":{"tags":["auth"],"summary":"Logout All","operationId":"logout_all_api_auth_logout_all_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutAllView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/link/metadata":{"get":{"tags":["link"],"summary":"Hosted-share metadata a daemon reads on `nerdit link refresh`","description":"Publish the hosted base domain. Unauthenticated, static, cacheable.\n\nNo session, no node, no rate limiter of its own: the answer is the same\nfor every caller and is already public in DNS and in the wildcard\ncertificate, so there is nothing here for a caller to be authorized\n*for*. A pre-P26 daemon never asks; a pre-P26 **cloud** answers 404,\nwhich the daemon maps to its own ``link.refresh_unsupported`` — so the\none thing this route must never do is exist and answer something else.","operationId":"metadata_api_link_metadata_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkMetadataView"}}}}}}},"/api/link/claim":{"post":{"tags":["link"],"summary":"Exchange a link code for a node identity","description":"Release the node identity a link code names, at most once per code.","operationId":"claim_api_link_claim_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkClaimRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkClaimResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"Gone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/link/device":{"post":{"tags":["link"],"summary":"Request a device link code a human can approve in the console","description":"Mint the two artifacts of one device request (D-X16-O1/O3/O5).\n\nUnauthenticated by design — nobody is signed in on the machine being\nlinked, which is the whole point — so this grants nothing on its own. It\nwrites one ``pending`` row that names no node and no account: the node\ndoes not exist until a human approves, and the approval runs the same\naccount-standing assertion the console mint runs (D-X16-O25). The most\nthis route can be made to produce in bulk is rows that expire, which is\nwhat the budgets above bound.\n\nBoth plaintexts exist only in the response object. The row keeps their\ndigests; the audit row keeps neither, and passes both to\n``forbidden_values`` so that any future edit which interpolates one into\nthe metadata raises here rather than shipping.\n\nTwo other things are captured, and the difference between them is the\nwhole of D-X16-O23. The three strings the daemon *reports* about itself\nare stored because the approval screen has to show a human something\nthey can recognise their own laptop by — bounded and control-stripped on\nthe way in, and labelled unverified on the way out, because this route\nis unauthenticated and a phisher writes whatever reads best. The address\nthe request came *from* is stored because it is the one requester fact\nthe requester cannot choose, and because it can only be observed here:\nby the time the approval screen loads, the only address the cloud can\nsee is the approver's own.","operationId":"request_device_link_api_link_device_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceLinkRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceLinkResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/link/device/poll":{"post":{"tags":["link"],"summary":"Ask whether a device link request has been answered yet","description":"Wait for a human, then release the same identity ``/claim`` releases.\n\nThe 200 body is constructed from :class:`LinkClaimResponse` itself, so\nan approved device link and an ``NL-`` claim are the same bytes on the\nwire and a daemon parses one thing (D-X16-O2).\n\nOrdering is the security of this route. The rate limit is charged first\nbecause it is the only control that exists before the caller has said\nanything. The cadence check comes next and answers ``slow_down`` without\ntouching the database — identically for a real code and an invented one,\nso it tells a fast prober nothing. Only then does the policy layer look\nthe row up, and its own first move is the constant-time comparison\nagainst the pinned credential (D-X16-O22): every answer that reveals\nstate sits behind it.","operationId":"poll_device_link_api_link_device_poll_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevicePollRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkClaimResponse"}}}},"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevicePollPending"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"Gone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth/authorize/context":{"get":{"tags":["oauth"],"summary":"Describe the pending MCP authorization the browser was handed","description":"Return what a grant would cover; reading never consumes or issues.","operationId":"authorize_context_api_oauth_authorize_context_get","parameters":[{"name":"rid","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":512,"title":"Rid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizeContextView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth/authorize/decision":{"post":{"tags":["oauth"],"summary":"Approve or deny one pending MCP authorization","description":"Consume the transaction and issue a code, or report the refusal.","operationId":"authorize_decision_api_oauth_authorize_decision_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizeDecisionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizeDecisionView"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth/connector":{"get":{"tags":["oauth"],"summary":"The MCP URL and registered client identifier for an assistant","description":"Describe this account's connector as this deployment would accept it.\n\nEvery value is answered by asking the same question the flow itself\nwould ask. The entitlement is the one that is easy to forget and the\nmost confusing to get wrong: ``/oauth/authorize`` refuses an account\nwithout Remote Link at the consent screen, so a page that printed a URL\nand a client identifier to such an account would be sending someone to\npaste two strings and hit ``entitlement_required`` with nothing on\nscreen explaining why. Unentitled reads report the refusal and no\nvalues.\n\nDeliberately *not* rate limited, unlike its two neighbours: the\n``/claude`` page polls it every few seconds for the connection dot, and\na budget tight enough to matter here would refuse the page rather than\nan attacker. It is a session-gated read of rows the caller already owns\n— a `GET /api/nodes` with two constants attached — so there is nothing\non it worth flooding for. Nothing bounding it is also why what it costs\nis a property of the route rather than a detail: the entitlement and\nthe node list are two independent questions and are asked at the same\ntime, and the third answer — the deployment's registered client — is\nthe same row for every account here, so it is read from the cached\nsnapshot rather than queried once per poll per open page.","operationId":"connector_api_oauth_connector_get","parameters":[{"name":"client_id","in":"query","required":false,"schema":{"anyOf":[{"enum":["claude","chatgpt","hermes"],"type":"string"},{"type":"null"}],"title":"Client Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/link/preauth":{"post":{"tags":["link"],"summary":"Enroll a machine with a pre-authorized link key","description":"Turn a key and a credential into the node identity the machine comes up as.\n\nThe response model is the claim route's own (D-X16-O7): a daemon that\ncan read one answer can read the other, and the shape being *the same\nclass* rather than a copy is what keeps it that way.\n\nEverything that decides anything is in\n:func:`~nerdit_cloud.nodes.preauth.consume_preauth_key`, including the\nD-X16-O9 evaluation order this route must not second-guess. What is\nhere is the metering, the shape check that makes passing the plaintext\nto ``forbidden_values`` safe, and the commit-then-raise the refusal path\nshares with every other route in this codebase.","operationId":"enroll_api_link_preauth_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreauthLinkRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkClaimResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/preauth-keys":{"get":{"tags":["nodes"],"summary":"List the pre-authorized link keys this account holds","description":"Answer metadata for every key on the account, newest first.\n\nRevoked and expired rows are included: this list is also the record of\nwhat the account has handed out, and a key that stopped working is\nexactly the row an owner comes here to find.","operationId":"list_keys_api_preauth_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreauthKeysView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["nodes"],"summary":"Mint a pre-authorized link key for the signed-in account","description":"Issue one key, and hand back its plaintext exactly once (D-X16-O7/O8).\n\nThe audit row records the key's id, its lifetime and its fan-out, and\npasses the plaintext as a forbidden value so that a future edit which\ninterpolated it into the metadata would fail loudly rather than ship a\nreadable enrollment credential in ``cloud_audit``.","operationId":"mint_api_preauth_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreauthKeyMintRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreauthKeyMintResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/preauth-keys/{key_id}":{"delete":{"tags":["nodes"],"summary":"Revoke a pre-authorized link key","description":"Withdraw a key's authority. Idempotent, and silent about what it did.\n\n``204`` for a key this call revoked, for one that was already revoked,\nfor an id that does not exist and for one belonging to another account:\na status that varied would make this an existence oracle for rows the\ncaller does not own. The audit row is written only when something\nactually changed, so a double click does not manufacture a second\nrevocation in the trail.\n\nNodes the key already enrolled are untouched — revoking the key that\ncreated a node does not revoke the node.","operationId":"revoke_api_preauth_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Key Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/managed/purchase":{"get":{"tags":["nodes"],"summary":"Show where the signed-in account's hosted machine purchase stands","description":"``none`` until the account asks; the state is derived, never stored.","operationId":"purchase_api_managed_purchase_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedPurchaseView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["nodes"],"summary":"Ask for a box by invoice, the manual path","description":"Record the request; the owner then invoices by hand.\n\nIdempotent on the account: a repeat answers ``200`` with the current\nstate and writes nothing. Refused ``409`` once every launch place is\ntaken (seats held plus open invoices).","operationId":"ask_api_managed_purchase_post","responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedPurchaseView"}}}},"200":{"description":"The account already asked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedPurchaseView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/managed/allocation":{"get":{"tags":["nodes"],"summary":"Show the signed-in account's hosted machine","description":"The account's allocation and how far provisioning got.","operationId":"show_api_managed_allocation_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedAllocationView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["nodes"],"summary":"Ask for a hosted machine for the signed-in account","description":"Create the account's allocation, or answer the one it already has.\n\nIdempotent on the account: one allocation per account, decided under\nthe account lock, so a double click or a retry answers ``200`` with the\nsame row. A ``failed`` allocation with no sandbox is replaced by a\nnew one (``201``, a new id).\n\nD-19: an account that is not paid gets a one-hour ``trial`` box\ninstead, while it has trials left (else ``403 trial_quota_used``) and\na trial place is free (else ``409 trial_boxes_busy``, worth retrying).","operationId":"allocate_api_managed_allocation_post","responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedAllocationView"}}}},"200":{"description":"The existing allocation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedAllocationView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"tags":["nodes"],"summary":"Delete the account's hosted machine","description":"Delete the account's hosted machine and unlink its node. No undo.\n\n``202`` while the provider delete is owed (``deleting``), ``200`` once\nnothing is left (``deleted``). Idempotent: a repeat changes nothing and\nwrites no audit row. Deliberately no payment and no standing check:\na lapsed or suspended owner must still be able to stop the cost.","operationId":"delete_api_managed_allocation_delete","responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedAllocationView"}}}},"200":{"description":"Deleted: nothing left to delete at the provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedAllocationView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/managed/allocation/wake":{"post":{"tags":["nodes"],"summary":"Wake the signed-in account's sleeping box","description":"Start the account's own box; never another account's (no id is taken).\n\n``202`` while it starts (a repeat answers the same), ``200`` when it is\non, ``404`` with no ready box, ``403`` for an inactive account, and\n``429`` when it cannot be started now (the start budget is spent, or a\nstart has lasted over two minutes). Works whatever the sleep setting.","operationId":"wake_api_managed_allocation_wake_post","responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedWakeView"}}}},"200":{"description":"The box was not asleep","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedWakeView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/managed/allocation/update":{"get":{"tags":["nodes"],"summary":"Show whether the signed-in account's box can be updated","description":"The box's version, the template's, and any update in progress or failed.","operationId":"update_status_api_managed_allocation_update_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedUpdateView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["nodes"],"summary":"Update the signed-in account's box to the available version","description":"Record the owner's update; the provisioner performs it. No id is taken.\n\n``202`` once accepted, and again (nothing written) while it runs.\n``409`` while the box is offline or busy, up to date, offered another\nversion, or being backed up; ``429`` within 3 minutes of the previous\naccepted click. Nothing is ever forced and never downgraded.","operationId":"update_box_api_managed_allocation_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedUpdateRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedUpdateView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/managed/allocation/backups":{"get":{"tags":["nodes"],"summary":"Show the backup health of the signed-in account's hosted machine","description":"Nightly backups of the account's newest hosted machine (tombstones included).","operationId":"backups_api_managed_allocation_backups_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagedBackupsView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/nodes/device-approvals/lookup":{"post":{"tags":["nodes"],"summary":"Read the pending device request a user code names","description":"Answer what the approval screen shows, before anybody has consented.\n\nA read: it charges nothing against the code's attempt budget, because a\nperson who lands on the page from the link their own terminal printed has\nnot presented a guess. The budget is spent by approve and deny, which are\npresentations; this route's backpressure is the pair of rate limits above.","operationId":"lookup_api_nodes_device_approvals_lookup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceApprovalRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceApprovalView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/device-approvals/approve":{"post":{"tags":["nodes"],"summary":"Approve a device request under the signed-in account","description":"Grant a machine a place under this account, on the credential it pinned.\n\nThe standing assertion, the node limit, the create-or-adopt and the\ncompare-and-set are all one transaction inside\n:func:`~nerdit_cloud.nodes.link.approve_device_request` — approval *is*\nthe browser consume of the ``NL-`` flow, relabelled (D-X16-O1), and it is\nthe mint-equivalent path, so an account that may not mint may not enroll a\nmachine by this door either (D-X16-O25).\n\nThe reported hostname is read here and passed down because it is the stem\nof the node's slug: it is the *display* half of the row, and the policy\nlayer takes it as an argument rather than trusting it to be present.","operationId":"approve_api_nodes_device_approvals_approve_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceApprovalRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceApprovalGrant"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/device-approvals/deny":{"post":{"tags":["nodes"],"summary":"Refuse a device request","description":"Settle a request as refused, so the machine waiting on it stops.\n\nNo standing assertion: saying no grants nothing, and the account most in\nneed of this button is one that did not start the flow at all — which is\nthe phishing case (D-X16-O23). A denial the daemon then polls is\n``403 link_code_denied``, which is what makes \"somebody said no\" different\nfrom \"this code means nothing\".","operationId":"deny_api_nodes_device_approvals_deny_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceApprovalRequest"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/navigation/authorize":{"post":{"tags":["nodes"],"summary":"Authorize a dashboard ticket for its requesting browser","operationId":"authorize_navigation_api_nodes_navigation_authorize_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NavigationAuthorizationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NavigationAuthorizationView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"Gone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes":{"get":{"tags":["nodes"],"summary":"List the nodes linked to the signed-in account","description":"Return the caller's active nodes with their live relay status.\n\nEach row carries its ``link_state``, read in the same statement as the\nrow itself (:func:`~nerdit_cloud.nodes.issuance.active_nodes_with_link_state`).\nThe console used to derive that by joining this list against the open\ncodes, which cannot be right for a node whose code was consumed or\nrevoked — those are not open — and is a refresh behind even when it is.","operationId":"list_nodes_api_nodes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodesView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/nodes/link-codes":{"get":{"tags":["nodes"],"summary":"List the open link codes of the signed-in account","description":"Return what the console needs to render pending-node cards.\n\nOnly open codes: a consumed one is a linked node and appears in\n``GET /api/nodes``, while an expired or revoked one is history the user\ncan do nothing with.","operationId":"list_link_codes_api_nodes_link_codes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkCodesView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["nodes"],"summary":"Mint a link code for the signed-in account","description":"Issue a code, and the node row it names, to this session (D-LC1/D-LC2).\n\nThe plaintext is in the answer and nowhere else: the audit row records\nthe code's id, its node and its expiry, and passes the plaintext as a\nforbidden value so that a future edit which interpolated it into the\nmetadata would fail loudly rather than ship a readable code in\n``cloud_audit``.","operationId":"mint_link_code_api_nodes_link_codes_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkCodeMintRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkCodeMintResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/link-codes/{link_code_id}":{"delete":{"tags":["nodes"],"summary":"Revoke an open link code of the signed-in account","description":"Close a code, and collect the node with it if nobody ever claimed it.\n\nAlways 204. A code that does not exist, one that belongs to another\naccount, and one that was already closed are answered identically, for\nthe two reasons unlink answers a repeat with 204: the caller asked for a\nstate the row is already in, and a route that distinguished them would\nturn a guessed id into a membership oracle. Only the request that\nactually performed the transition writes the audit row.","operationId":"revoke_link_code_api_nodes_link_codes__link_code_id__delete","parameters":[{"name":"link_code_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Link Code Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/dashboard":{"get":{"tags":["nodes"],"summary":"Open an owned node dashboard through a one-time ticket","operationId":"open_dashboard_api_nodes__node_id__dashboard_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"302":{"description":"Redirect to the one-time node-host exchange"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/shares":{"get":{"tags":["nodes"],"summary":"List what an owned node currently shares","description":"Ask the node itself what it shares, and say what this cloud will serve.\n\nNot a projection: this deployment stores no share state (D-P26-H1)\nother than who else may open a private share, which is\n``…/access-rules`` (ADR-W9), so the list is one bounded daemon-API read\nover the tunnel, made\nserver-side because the browser cannot make it — the node-proxy\ncredential is ``__Host-`` on the node host and this SPA is on ``app.``\n(D-HC-7). Two facts are joined onto it from *here*, because they are the\ntwo the cloud owns: whether the account may publish at all, and whether\nthe owner has paused anonymous access.\n\nAn offline node answers an empty list rather than an error. Its shares\nstill exist — the daemon holds them — and the card says \"offline\"; a 502\nis reserved for a node that is online and did not answer, which is a\nfault rather than a state.","operationId":"list_shares_api_nodes__node_id__shares_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeSharesView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/shares/{service_name}/open":{"get":{"tags":["nodes"],"summary":"Open an owned node's shared service through a one-time ticket","description":"Mint a ticket bound to ``<service>--<slug>.<nodes_base_domain>``.\n\nThe name is validated against the daemon's own service grammar before\nanything else, and a failure is the same 404 an unknown node gets: what\nis not a service name cannot be a hosted label, and answering anything\nmore specific would describe this deployment's naming rules to whoever\nasked. Whether a service by that name *exists*, or is shared, is not\nasked here at all — the daemon re-reads its own share row on every\nstream and answers its byte-identical ``share.not_shared`` for a miss,\nso a ticket to nothing costs its holder one 404 and this route no\nknowledge it would otherwise have to keep in sync.","operationId":"open_share_api_nodes__node_id__shares__service_name__open_get","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"302":{"description":"Redirect to the one-time hosted-host exchange"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/shares/{service_name}/access":{"post":{"tags":["nodes"],"summary":"Publish an owned node's app, or make it private again","description":"Publish one app on an owned machine, or make it private again.\n\nThe change is written on the **node** — this cloud stores no share state\nbeyond the access rules of ADR-W9 — so the call is one bounded ``PUT``\ndown the tunnel, and the answer is\nthe share as the node now reports it: the same row shape\n``GET …/shares`` lists, so a console can redraw what it just changed\nwithout re-reading the card.\n\nRefused, in this order: a node that is not the caller's own (``404``); a\nname outside the service grammar (``422``); an account that is suspended\nor being deleted (``403``); a node that is offline (``409``).\nPast that the node's own refusal is forwarded with\nits own sentence and its ``daemon_code`` in ``details``.","operationId":"set_share_access_api_nodes__node_id__shares__service_name__access_post","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareAccessRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedShareView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/hosted-disabled":{"post":{"tags":["nodes"],"summary":"Pause or resume anonymous access to an owned node's public shares","description":"Flip the takedown switch, then end the streams it just de-authorized.\n\nThe column is the whole mechanism (D-HC-9): the public choke point reads\nit on every request and the sweep reads it every pass, so the switch is\nalready effective the moment this transaction commits. Terminating the\nlive streams is what makes it effective *now* rather than at the end of\nwhatever an anonymous visitor is currently streaming — and it happens\nafter the commit, for the reason unlink closes its streams after its\nown: a ``close_stream`` frame outlives the rollback of the transaction\nthat sent it, so a transaction that might still roll back must not send\none.\n\nOnly the anonymous streams (:meth:`ActiveStreamRegistry.by_hosted_node`)\n— never the owner's own dashboard or share session on the same node.\nResuming ends nothing: there is nothing de-authorized to end.","operationId":"set_hosted_disabled_api_nodes__node_id__hosted_disabled_post","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedDisabledRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedDisabledView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}":{"patch":{"tags":["nodes"],"summary":"Rename an owned machine's console label","description":"Write the owner's own label for their machine, and nothing else.\n\nTask 12 §\"Renaming a node\", work item D6. One column moves. The\n*address* — ``<slug>.<nodes_base_domain>``, and the hosted host\n``<app>--<slug>`` built from the same slug — is untouchable here and\nthis route has no expression for touching it: the slug is identity\nrather than a name, guarded by the generator, by\n:func:`~nerdit_cloud.nodes.hosted.assert_slug` and by the\n``slug_no_double_hyphen`` check constraint, and renaming it would\ninvalidate live URLs, DNS records and the certificate they sit under.\nThe console says so on the form, beside the unchanged hostname, because\nthis is the point at which the label and the address part company\npermanently.\n\n**Standing-gated, not entitlement-gated (D-NC8).** The refusal ladder\nstops at :func:`assert_account_active`: a Free account reaches the same\nmachine's own dashboard and renames it there for nothing, so a console\nthat charged for the label would be a harder gate than the thing it is a\nconsole for. There is no ``hosted_public_entitled`` read on this path,\ndeliberately, and a test pins the absence.\n\nNo daemon call either, for the same reason there is no tunnel byte in\nthe takedown switch: the label is cloud state. An offline machine renames\nexactly as well as a live one, which is what makes this the one thing a\nperson can tidy up while the box is off (owner decision, 2026-08-29 —\n``nerdit`` locally keeps calling the machine whatever the daemon calls\nit, and making the two agree would be a ``node-link/v1`` write path and a\nfixture bump for a cosmetic field).\n\nThis does not violate D-NC10. That rule governs data *from* the node; a\nlabel the owner authored is the owner's own data *about* their node — the\nsame category as the column that already exists.","operationId":"rename_node_api_nodes__node_id__patch","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeRenameRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeRenameView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"}}}},"/api/nodes/claim":{"post":{"tags":["nodes"],"summary":"Claim a link code for the signed-in account","description":"Consume a link code and transfer the node it names to this account.","operationId":"claim_api_nodes_claim_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeClaimRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeClaimResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"Gone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Too Many Requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/unlink":{"post":{"tags":["nodes"],"summary":"Unlink a node from the signed-in account","description":"Revoke a node the session owns and its credentials, then tell the relay.\n\nThe cascade runs under the node's own compare-and-set and inside the\nsame transaction, so there is no moment in which the node is unlinked\nwhile a connector's refresh family still reads as live. It is\nbookkeeping rather than the thing that stops the access — every\ncredential path re-reads the node row on every presentation — but it is\nwhat keeps ``/security`` truthful, what lets the purges reclaim the\nrows, and what writes the per-subject audit detail underneath this\nroute's ``NODE_UNLINK`` umbrella (plan D-P6.2/D-P6.12).\n\nThe credentials' *streams* are ended after that transaction commits,\nalongside the relay tombstone rather than inside the cascade: a\n``close_stream`` frame survives the rollback of the transaction that\nsent it, so a retryable transaction must not send one. The consumers are\nkilled before the ``revoke_node`` frame so that what they are told is\n``access_revoked`` rather than whatever a dropped tunnel synthesises.","operationId":"unlink_api_nodes__node_id__unlink_post","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/projects":{"get":{"tags":["projects"],"summary":"List Projects","operationId":"list_projects_api_projects_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":100,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^prj_[a-z2-7]{16}$"},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDirectory"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["projects"],"summary":"Create Project","operationId":"create_project_api_projects_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/projects/{project_id}":{"get":{"tags":["projects"],"summary":"Get Project","operationId":"get_project_api_projects__project_id__get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","minLength":20,"maxLength":20,"pattern":"^prj_[a-z2-7]{16}$","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["projects"],"summary":"Rename Project","operationId":"rename_project_api_projects__project_id__patch","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","minLength":20,"maxLength":20,"pattern":"^prj_[a-z2-7]{16}$","title":"Project Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectRename"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/projects/{project_id}/live":{"get":{"tags":["projects"],"summary":"Live Project","operationId":"live_project_api_projects__project_id__live_get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","minLength":20,"maxLength":20,"pattern":"^prj_[a-z2-7]{16}$","title":"Project Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectLiveView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/projects/{project_id}/variables":{"put":{"tags":["projects"],"summary":"Set Variables","operationId":"set_variables_api_projects__project_id__variables_put","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","minLength":20,"maxLength":20,"pattern":"^prj_[a-z2-7]{16}$","title":"Project Id"}},{"name":"service","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":20,"pattern":"^(?!.*--)[a-z0-9]([a-z0-9-]{0,18}[a-z0-9])?$"},{"type":"null"}],"title":"Service"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectVariablesSet"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectVariablesResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/services":{"get":{"tags":["nodes"],"summary":"List every service on an owned node","description":"Ask the node what it is running, and report all of it (D-NC3/D-NC4).\n\nBetween one and :data:`SERVICE_MAX_PAGES` bounded reads, following the\ndaemon's cursor, because one 200-row page does not fit under the client's\n64 KiB cap. Nothing is stored on the way through (D-NC10) and nothing is\nfiltered out on the way back (D-NC13).\n\nNo entitlement gate on the read: ``OwnedNodePolicy`` has already refused\nevery node that is not the caller's own, and this is a page render. The\nfour verbs below are where standing is asked about, which is where the\nexisting surfaces ask about it too.","operationId":"list_services_api_nodes__node_id__services_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeServicesView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/services/{service_name}":{"get":{"tags":["nodes"],"summary":"Service Detail","operationId":"service_detail_api_nodes__node_id__services__service_name__get","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeServiceView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/services/{service_name}/logs":{"get":{"tags":["nodes"],"summary":"Service Logs","operationId":"service_logs_api_nodes__node_id__services__service_name__logs_get","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceLogsView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/services/{service_name}/restart":{"post":{"tags":["nodes"],"summary":"Restart a service on an owned node","operationId":"node_service_restart_api_nodes__node_id__services__service_name__restart_post","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeServiceActionView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service_name}/stop":{"post":{"tags":["nodes"],"summary":"Stop a service on an owned node","operationId":"node_service_stop_api_nodes__node_id__services__service_name__stop_post","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeServiceActionView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service_name}/redeploy":{"post":{"tags":["nodes"],"summary":"Redeploy a service on an owned node from its recorded source","operationId":"node_service_redeploy_api_nodes__node_id__services__service_name__redeploy_post","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeServiceActionView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service_name}/rollback":{"post":{"tags":["nodes"],"summary":"Roll a service on an owned node back to its previous image","operationId":"node_service_rollback_api_nodes__node_id__services__service_name__rollback_post","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeServiceActionView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/secrets/{service}":{"get":{"tags":["nodes"],"summary":"List the secret key names one owned node holds for a scope","description":"Ask the node which keys it holds, and report their names (S1).\n\nNames only, because names are all the daemon will say. ``shared`` is\npassed through as the user typed it; every refusal is the node's own.","operationId":"list_secret_names_api_nodes__node_id__secrets__service__get","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeSecretKeysView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}},"post":{"tags":["nodes"],"summary":"Set secret values on one owned node for a scope (names come back)","description":"Forward one set to the node and answer with its key names (S2).\n\nSet only; replacing is overwriting. CSRF and the rate limit gate it\n(rule 6), standing and never the plan (D-NC8). The value is in\n``payload`` and leaves this function only as the daemon request body:\nit is never audited (rule 2), logged (rule 3), echoed (rule 4) or\nretried (rule 5), and a refusal is the daemon's own words (rule 7).","operationId":"set_secret_values_api_nodes__node_id__secrets__service__post","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeSecretValuesWrite"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeSecretKeysView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service}/ai":{"get":{"tags":["nodes"],"summary":"Read one app's AI provider bindings and the key names they can use","description":"Ask the node for the app's ``[ai.*]`` view, its key names and the shared scope.","operationId":"read_ai_api_nodes__node_id__services__service__ai_get","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}},"put":{"tags":["nodes"],"summary":"Write one app's AI provider bindings and restart it","description":"Forward the bindings under ``If-Match`` and restart the app.\n\nCSRF, a rate limit and account standing (never the plan, D-NC8). The\ndaemon's refusals pass through in its words: ``409 config.stale`` when\nthe app changed since the read, ``422 deploy.invalid_ai`` for a spec it\nwill not take (``config.invalid`` only for one that is not a table).","operationId":"write_ai_api_nodes__node_id__services__service__ai_put","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiWrite"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiWriteView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/nodes/{node_id}/services/{service}/hooks":{"get":{"tags":["nodes"],"summary":"List one app's inbound webhooks on an owned node","description":"Ask the machine for the app's webhooks; offline and older daemons are states.","operationId":"list_hooks_api_nodes__node_id__services__service__hooks_get","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeHooksView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}},"post":{"tags":["nodes"],"summary":"Add an inbound webhook to one app (the secret goes through S2)","description":"Forward the webhook with a fresh Idempotency-Key; refusals in the daemon's words.\n\nNo cloud audit row: the daemon audits ``hook.created`` and D-WH-11 keeps\nActivity to failures and Redeliver.","operationId":"add_hook_api_nodes__node_id__services__service__hooks_post","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeHookWrite"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeHookSetView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service}/hooks/{name}":{"delete":{"tags":["nodes"],"summary":"Remove one app's inbound webhook by name (idempotent)","description":"Revoke the webhook on the machine; its S2 secret stays (no remove in S2).","operationId":"remove_hook_api_nodes__node_id__services__service__hooks__name__delete","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeHookRemovedView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service}/hooks/deliveries":{"get":{"tags":["nodes"],"summary":"List one hook's deliveries on an owned node (metadata only)","operationId":"list_hook_deliveries_api_nodes__node_id__services__service__hooks_deliveries_get","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}},{"name":"hook_id","in":"query","required":true,"schema":{"type":"string","title":"Hook Id"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Before"}},{"name":"before_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Before Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HookDeliveriesView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"}}}},"/api/nodes/{node_id}/services/{service}/hooks/deliveries/{delivery_id}/redeliver":{"post":{"tags":["nodes"],"summary":"Queue a failed delivery again while its body is held","description":"Requeue under the same id and ``received_at``.\n\nThe daemon's 25 h Received-At window and the app's dedupe both hold,\nbecause nothing the app sees changes but the attempt.","operationId":"redeliver_hook_delivery_api_nodes__node_id__services__service__hooks_deliveries__delivery_id__redeliver_post","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"delivery_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Delivery Id"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HookDeliveryView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"}}}},"/api/nodes/{node_id}/ai-gateway":{"get":{"tags":["nodes"],"summary":"Read one machine's AI gateway models and the key names they can use","description":"Ask the node for its gateway state, its aliases and its shared key names.","operationId":"read_gateway_api_nodes__node_id__ai_gateway_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiGatewayView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/ai-gateway/provider-models":{"post":{"tags":["nodes"],"summary":"List the models a provider offers for one of the machine's keys","description":"The daemon calls the provider with the named key; only model ids come back.\n\nA POST under the mutation policy although nothing is stored: the daemon\nsends the key to whatever host ``base_url`` names, so a cross-site GET\nriding the session cookie could have it sent to an attacker's host.\nCSRF, the write limit and standing apply as to a write. A daemon\nwithout the route (before 0.8.5) answers 404, which passes through:\nthe console then asks for the model name instead.","operationId":"provider_models_api_nodes__node_id__ai_gateway_provider_models_post","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiProviderModelsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiProviderModelsView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/nodes/{node_id}/ai-gateway/models/{alias}":{"put":{"tags":["nodes"],"summary":"Add a model to the machine's AI gateway, or replace one","description":"Forward ``PUT /api/ai-gateway/routes/{alias}`` with a fresh ``Idempotency-Key``.\n\nThe key travels as its ``${secrets.shared.KEY}`` reference. The daemon's\nrefusals pass through in its words (``ai_gateway.base_url_invalid``,\n``ai_gateway.secret_ref_invalid``, ``ai_gateway.alias_invalid``).","operationId":"write_model_api_nodes__node_id__ai_gateway_models__alias__put","parameters":[{"name":"alias","in":"path","required":true,"schema":{"type":"string","title":"Alias"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiModelWrite"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiModelWriteView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}},"delete":{"tags":["nodes"],"summary":"Remove a model from the machine's AI gateway","description":"Forward ``DELETE /api/ai-gateway/routes/{alias}``.\n\n``409 ai_gateway.alias_in_use`` passes through naming the running apps;\n``force`` removes anyway (their AI calls then fail until repointed).","operationId":"remove_model_api_nodes__node_id__ai_gateway_models__alias__delete","parameters":[{"name":"alias","in":"path","required":true,"schema":{"type":"string","title":"Alias"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Force"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiModelRemoveView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/nodes/{node_id}/ai-gateway/usage":{"get":{"tags":["nodes"],"summary":"Read one machine's AI usage per app and per model","description":"Forward ``GET /api/ai-gateway/usage?days=``, summed per (app, model).\n\nA daemon from before 0.8.2 answers 404, which passes through.","operationId":"read_usage_api_nodes__node_id__ai_gateway_usage_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"description":"UTC days back, today included.","default":7,"title":"Days"},"description":"UTC days back, today included."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiUsageView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/ai-gateway/keys":{"get":{"tags":["nodes"],"summary":"List the apps holding a key to the machine's AI gateway","description":"Forward ``GET /api/ai-gateway/keys`` (active keys only): app names and dates.","operationId":"list_app_keys_api_nodes__node_id__ai_gateway_keys_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiAppKeysView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/ai-gateway/keys/{service}":{"delete":{"tags":["nodes"],"summary":"Revoke an app's key to the machine's AI gateway","description":"Forward ``DELETE /api/ai-gateway/keys/{service}`` with a fresh ``Idempotency-Key``.\n\nThe app's AI calls answer 401 until it restarts and gets a new key. The\ndaemon's 404 (no such app) and 403 (an app the console's link does not\nown, e.g. deployed locally: daemon 0.8.3+) pass through in its words.","operationId":"revoke_app_key_api_nodes__node_id__ai_gateway_keys__service__delete","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAiAppKeyRevokeView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/nodes/{node_id}/services/{service}/domains":{"get":{"tags":["nodes"],"summary":"List the custom domains one owned node serves for a service","description":"Ask the node what names this app answers to (D-NC9, D-NC10).\n\nOwnership only: ``OwnedNodePolicy`` has already refused every node that\nis not the caller's, and this is a page render — no standing check, no\nentitlement, no audit row, and nothing stored on the way through.","operationId":"list_domains_api_nodes__node_id__services__service__domains_get","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeDomainsView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service}/domains/{domain}":{"put":{"tags":["nodes"],"summary":"Bind a custom domain to an owned node's app","description":"Bind one name, on the node, and report the row as the node now has it.\n\nRefused, in this order: a node that is not the caller's own (404); a name\noutside the service grammar (422); a segment this router cannot put on a\nframe (422, the transport class — never a judgement about the name); an\naccount that is not in good standing (403); a node that is offline (409,\nbecause its domains keep serving and only *managing* them is\nunavailable). Past that the node's own refusal is forwarded, with the two\nACME cases rendered as remedies.","operationId":"bind_domain_api_nodes__node_id__services__service__domains__domain__put","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainBindRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainBoundView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}},"delete":{"tags":["nodes"],"summary":"Release a custom domain from an owned node's app","description":"Release one name. Idempotent on the node: ``removed: false`` is normal.\n\nThe same pipeline the bind runs, minus the body. One extra rule on the\nway out: the daemon echoes its ``<refused>`` sentinel for a name that did\nnot validate, and this route renders that as ``null`` rather than as\nsomething a reader could mistake for a name.","operationId":"release_domain_api_nodes__node_id__services__service__domains__domain__delete","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainReleasedView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service}/domains/{domain}/check":{"get":{"tags":["nodes"],"summary":"Check where one bound domain points and whether this machine is reachable from the internet","description":"Look, once, live, and remember nothing (task 12 D2).\n\n**The order below is the security property**, not an implementation\nconvenience, so it is written out rather than left to be read off the\ncode:\n\n1. the service grammar, then the transport class — the same two refusals\n   the writes make, before a tunnel byte;\n2. the rate limit, before a tunnel byte too: a check is two daemon reads\n   and up to three network operations, and the cheapest place to refuse\n   a flood is before any of them;\n3. the node's **own list of bound names**, and a 404 for anything not on\n   it. This is what keeps the route from being a request-forgery\n   primitive: the caller cannot get this far with a name the machine\n   does not already serve;\n4. only then the capabilities read, the DNS lookup, and the probes;\n5. and the probes go to **the address the relay observed this machine\n   dialling out from**, canonicalized, and only when that address is\n   globally routable. Never to a DNS answer. The caller controls the\n   name and therefore controls the answer; it does not control what this\n   deployment observed.\n\nA read, so no standing check, no audit row, and nothing written — the\none departure being the 409 below.","operationId":"check_domain_api_nodes__node_id__services__service__domains__domain__check_get","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainCheckView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service}/cloud-domains":{"get":{"tags":["nodes"],"summary":"List one app's cloud-fronted custom domains","operationId":"list_cloud_domains_api_nodes__node_id__services__service__cloud_domains_get","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CloudDomainView"},"title":"Response List Cloud Domains Api Nodes  Node Id  Services  Service  Cloud Domains Get"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"}}},"post":{"tags":["nodes"],"summary":"Add a cloud-fronted custom domain to one app (Pro)","operationId":"add_cloud_domain_api_nodes__node_id__services__service__cloud_domains_post","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloudDomainWrite"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloudDomainView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service}/cloud-domains/{host}/check":{"post":{"tags":["nodes"],"summary":"Ask Cloudflare again about one cloud-fronted custom domain","operationId":"check_cloud_domain_api_nodes__node_id__services__service__cloud_domains__host__check_post","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"host","in":"path","required":true,"schema":{"type":"string","title":"Host"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloudDomainView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/services/{service}/cloud-domains/{host}":{"delete":{"tags":["nodes"],"summary":"Remove one cloud-fronted custom domain","operationId":"remove_cloud_domain_api_nodes__node_id__services__service__cloud_domains__host__delete","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}},{"name":"host","in":"path","required":true,"schema":{"type":"string","title":"Host"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/app-templates":{"get":{"tags":["nodes"],"summary":"List the starter templates an owned node can deploy","description":"One bounded read of the machine's template catalog. Not audited (D-NC11).","operationId":"list_app_templates_api_nodes__node_id__app_templates_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeAppTemplatesView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/nodes/{node_id}/app-templates/{template_id}/deploy":{"post":{"tags":["nodes"],"summary":"Deploy a starter template on an owned node","description":"Gate, call, translate, audit: ``node_services.run_action``'s order.\n\nNo ``pusher.on_node_apps_changed``: that nudge index and token push are\nGitHub's, and a template deploy mints no token and sets no auto-deploy.","operationId":"deploy_app_template_api_nodes__node_id__app_templates__template_id__deploy_post","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeTemplateDeployRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeTemplateDeployView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/capabilities":{"get":{"tags":["nodes"],"summary":"Read what one machine can do","operationId":"read_capabilities_api_nodes__node_id__capabilities_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeCapabilitiesView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/cluster/stats":{"get":{"tags":["nodes"],"summary":"Read one machine's GPU and service counts","operationId":"read_cluster_stats_api_nodes__node_id__cluster_stats_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeClusterStatsView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/system/disk":{"get":{"tags":["nodes"],"summary":"Read what Nerdit uses on one machine's disk","operationId":"read_system_disk_api_nodes__node_id__system_disk_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeDiskView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/gpus":{"get":{"tags":["nodes"],"summary":"List one machine's GPUs","operationId":"list_gpus_api_nodes__node_id__gpus_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeGpusView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/doctor":{"get":{"tags":["nodes"],"summary":"Run one machine's health checks","operationId":"read_doctor_api_nodes__node_id__doctor_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeDoctorView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/proxy/status":{"get":{"tags":["nodes"],"summary":"Read one machine's proxy health","operationId":"read_proxy_status_api_nodes__node_id__proxy_status_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeProxyStatusView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/events":{"get":{"tags":["nodes"],"summary":"Read one page of one machine's event feed, newest first","operationId":"list_events_api_nodes__node_id__events_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^[0-9]{1,19}$"},{"type":"null"}],"title":"Cursor"}},{"name":"types","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512,"pattern":"^[a-z_]+\\.[a-z_]+(,[a-z_]+\\.[a-z_]+){0,9}$"},{"type":"null"}],"title":"Types"}},{"name":"service","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Service"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeEventsView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/models":{"get":{"tags":["nodes"],"summary":"List one page of one machine's AI models","operationId":"list_models_api_nodes__node_id__models_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^[\\x21-\\x7e]{1,512}$"},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeModelsView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/shares/{service_name}/access-rules":{"get":{"tags":["nodes"],"summary":"List who besides the owner may open an owned machine's private app","description":"The app's active rules, and the lock: one machine call when rules exist.","operationId":"list_access_rules_api_nodes__node_id__shares__service_name__access_rules_get","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessRulesView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}},"post":{"tags":["nodes"],"summary":"Let an address, a Workspace domain or a group open an owned machine's private app","description":"Add one rule, or answer the identical active one: a repeat is not an error.\n\nA paid account only (decision k), on any machine. When the app is not\nshared yet it is shared **private** on the machine first, and a private\nshare is locked hosted-only; a share or lock the machine does not let\nthe cloud make is refused with the command to run there.\nWith ``invite`` the address is emailed after this answer is sent.\nA group, by its id, is attached only while groups are on (404 off).","operationId":"add_access_rule_api_nodes__node_id__shares__service_name__access_rules_post","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessRuleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessRuleAdded"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"}}}},"/api/nodes/{node_id}/shares/{service_name}/access-rules/{rule_id}":{"delete":{"tags":["nodes"],"summary":"Revoke one access rule, its visitor sessions and their open streams","description":"Always 204: unknown, another app's and already revoked answer alike.","operationId":"revoke_access_rule_api_nodes__node_id__shares__service_name__access_rules__rule_id__delete","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"rule_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Rule Id"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/shares/{service_name}/access-log":{"get":{"tags":["nodes"],"summary":"Page backwards through who signed in to an owned machine's private app","description":"Newest first, 90 days; open to an unpaid account (lot 7, D4).","operationId":"app_access_log_api_nodes__node_id__shares__service_name__access_log_get","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Before"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessLogView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/nodes/{node_id}/shares/{service_name}/sessions/{session_id}":{"delete":{"tags":["nodes"],"summary":"End one visitor session of an owned machine's private app","description":"Always 204: unknown, another app's, ended and expired answer alike.\n\nNot a ban: the rule stands, so the person may sign in again.","operationId":"end_visitor_session_api_nodes__node_id__shares__service_name__sessions__session_id__delete","parameters":[{"name":"service_name","in":"path","required":true,"schema":{"type":"string","title":"Service Name"}},{"name":"session_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Session Id"}},{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/access/log":{"get":{"tags":["nodes"],"summary":"Page backwards through who signed in to this account's private apps","description":"Every machine of the account, unlinked ones included (D9).","operationId":"account_access_log_api_access_log_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Before"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessLogView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"}}}},"/api/access/groups":{"get":{"tags":["nodes"],"summary":"List this account's access groups and their people","description":"Open to an unpaid account: reads are free (decision 4).","operationId":"list_access_groups_api_access_groups_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessGroupsView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"tags":["nodes"],"summary":"Create an access group","description":"A paid account only; at most 20 groups, names unique per account.","operationId":"create_access_group_api_access_groups_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessGroupRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessGroupView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/access/groups/{group_id}":{"patch":{"tags":["nodes"],"summary":"Rename an access group","description":"Another account's group answers 404, as an unknown one.","operationId":"rename_access_group_api_access_groups__group_id__patch","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessGroupRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessGroupView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"}}},"delete":{"tags":["nodes"],"summary":"Delete an access group: detached everywhere, its sessions ended","description":"Always 204: unknown, deleted and another account's answer alike.","operationId":"delete_access_group_api_access_groups__group_id__delete","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/access/groups/{group_id}/members":{"post":{"tags":["nodes"],"summary":"Add an address or a Workspace domain to an access group","description":"Nobody is emailed (decision 2). A repeat answers the same person.","operationId":"add_access_group_member_api_access_groups__group_id__members_post","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessMemberRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessMemberView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"}}}},"/api/access/groups/{group_id}/members/{member_id}":{"delete":{"tags":["nodes"],"summary":"Remove one person from an access group and end their sessions through it","description":"Always 204: unknown, another group's and already removed answer alike.","operationId":"remove_access_group_member_api_access_groups__group_id__members__member_id__delete","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Group Id"}},{"name":"member_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Member Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/access/people":{"get":{"tags":["nodes"],"summary":"List everyone let into this account's private apps, and how","operationId":"list_access_people_api_access_people_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessPeopleView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/access/people/remove":{"post":{"tags":["nodes"],"summary":"Take an address or a domain off every app and group of this account","description":"Always 204 once valid. An address that came in through a Workspace\ndomain is signed out, and may sign in again while the domain is let in.","operationId":"remove_access_person_api_access_people_remove_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessMemberRequest"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Unprocessable Content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/access/apps":{"get":{"tags":["nodes"],"summary":"List this account's private apps that someone else may open","description":"No machine is asked (decision 8).","operationId":"list_access_apps_api_access_apps_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessAppsView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/security":{"get":{"tags":["security"],"summary":"Every credential this account has issued, and what can end it","description":"Answer what reaches this account's node, and on whose authority.\n\nOne session, three reads, no relay: everything here is durable state,\nso — unlike ``GET /api/nodes``, which merges in a liveness projection —\nthis page is the same answer whether or not a daemon is connected.","operationId":"security_overview_api_security_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecurityView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/security/account-mcp-grants/{grant_id}/access":{"put":{"tags":["security"],"summary":"Replace Account Access","operationId":"replace_account_access_api_security_account_mcp_grants__grant_id__access_put","parameters":[{"name":"grant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Grant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountGrantAccessRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountGrantAccessView"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/security/account-mcp-grants/{grant_id}/nodes":{"put":{"tags":["security"],"summary":"Replace Account Nodes","operationId":"replace_account_nodes_api_security_account_mcp_grants__grant_id__nodes_put","parameters":[{"name":"grant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Grant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountGrantNodesRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountGrantNodesView"}}}},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/security/account-mcp-grants/{grant_id}/revoke":{"post":{"tags":["security"],"summary":"Revoke Account Grant","operationId":"revoke_account_grant_api_security_account_mcp_grants__grant_id__revoke_post","parameters":[{"name":"grant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Grant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantRevocationView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/security/grants/{consent_id}/revoke":{"post":{"tags":["security"],"summary":"Withdraw one grant and every connection it authorized","description":"Disconnect one app from one node: the consent and its families go.","operationId":"revoke_grant_api_security_grants__consent_id__revoke_post","parameters":[{"name":"consent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Consent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantRevocationView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/security/families/{family_id}/revoke":{"post":{"tags":["security"],"summary":"End one connection, leaving the grant behind it in place","description":"The narrow lever: one connector goes, the app stays approved.","operationId":"revoke_family_api_security_families__family_id__revoke_post","parameters":[{"name":"family_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Family Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantRevocationView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/security/proxy-sessions/{session_id}/revoke":{"post":{"tags":["security"],"summary":"Close one node-dashboard session","description":"End one browser's access to one node dashboard; stay signed in here.","operationId":"revoke_proxy_session_api_security_proxy_sessions__session_id__revoke_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxySessionRevocationView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/security/revoke-all":{"post":{"tags":["security"],"summary":"Revoke every remote credential this account has issued","description":"Cut off every connector, dashboard session and guest session, and nothing else.\n\nNo 404 branch: the subject is the caller's own account, which the\nsession already proved exists. An account with nothing left to revoke\nrevokes zero, exactly as a repeat of any other scope does, though a\nstream whose credential is already dead still closes.","operationId":"revoke_all_api_security_revoke_all_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountRevocationView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/account/email-preferences":{"get":{"tags":["account"],"summary":"This account's mail preferences and the address they apply to","description":"Read the preference row, creating it on this first read if need be.\n\nThe row is minted lazily rather than backfilled by the migration for\nthe reason ``email/preferences.py`` gives: a backfill would write a\nbearer token for every account that predates the table, most of which\nwill never receive a marketing mail. So a read is a write, exactly\nonce, and the column defaults are what a missing row already meant.","operationId":"read_email_preferences_api_account_email_preferences_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailPreferencesView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"tags":["account"],"summary":"Update this account's mail preferences","description":"Write the submitted fields, with provenance for what changed.\n\nOnly a *transition* of the marketing flag writes anything beyond the\nrow: re-sending the state a page is already showing must not append a\nsecond grant, or the evidence table fills with events nobody caused.\nThe consent row is appended, never updated — a grant that rewrote the\nprevious revoke in place would destroy the only proof of what happened\nbetween them.\n\n``esp_state`` is marked ``pending`` on a transition only when a sync is\nactually owed against a contact that exists: this deployment knows an\n``esp_contact_id`` for the person. With no contact the audience entry\ndoes not exist yet, and a ``pending`` would be a drain forever chasing\nnothing — or, once a re-grant restores a contact id, one deleting the\naudience member of somebody who just opted in.\n\nNo provider call happens here, for the rule the deletion route states\nat length: a network hop under a row lock is how one slow vendor\nbecomes a stalled database.","operationId":"update_email_preferences_api_account_email_preferences_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailPreferencesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailPreferencesView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/delete":{"post":{"tags":["account"],"summary":"Delete the signed-in account and erase its personal data","description":"Revoke everything this account issued, then erase the person from it.\n\nNo 404 branch, for ``revoke-all``'s reason: the subject is the caller's\nown account, which the session already proved exists. No idempotent\nrepeat either, and that is the difference — the second press arrives\nwithout a session to authenticate, so it is a 401 rather than a set of\nzeros.","operationId":"delete_account_api_account_delete_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountDeletionView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/billing":{"get":{"tags":["billing"],"summary":"This account's plan, renewal state, and whether Pro is on sale","description":"Answer from the projection, never from the provider.\n\nWhich is what makes this page and the relay agree by construction: they\nare reading the same row, so a person is never shown an entitlement the\ngateway would refuse a second later.","operationId":"billing_overview_api_billing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/billing/checkout":{"post":{"tags":["billing"],"summary":"Start a hosted checkout for Pro","description":"Mint a checkout URL for an account that does not already have one.","operationId":"create_checkout_api_billing_checkout_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CheckoutRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Service Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/portal":{"post":{"tags":["billing"],"summary":"Open a customer-portal session for this account","description":"Refuse an account the provider has never met, then hand over a URL.\n\nThe local ``polar_customer_id`` is the gate rather than the key: it is\nhow this deployment knows a Polar customer exists, while the session\nitself is minted against the external id so a stale projection can\nnever open somebody else's portal.","operationId":"create_portal_api_billing_portal_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalSessionView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Service Unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/billing/webhook":{"post":{"tags":["billing"],"summary":"Accept one signed provider delivery","description":"Verify, resolve current state, project, acknowledge.\n\nNo session, no CSRF exemption to write down: the request carries no\ncookie, so ``CSRFMiddleware`` passes it through untouched and the\nsignature is the entire admission decision.","operationId":"webhook_api_billing_webhook_post","responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookAck"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/email/unsubscribe/{token}":{"post":{"tags":["email"],"summary":"Unsubscribe from marketing email using a token from a message","description":"Withdraw marketing consent. No session, no CSRF, idempotent.\n\nThe whole mutation is one short transaction: flip the flag, append the\nprovenance, mark the ESP owed a contact deletion. The provider is never\ncalled from here — the standing drain (task 11 E4) is what talks to\nResend, on the far side of a commit, exactly as the deletion tails do.","operationId":"unsubscribe_api_email_unsubscribe__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnsubscribeView"}}}},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Too Many Requests"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/audit":{"get":{"tags":["audit"],"summary":"Page backwards through this account's recorded activity","description":"Return one page of the caller's own trail, newest first.","operationId":"audit_page_api_audit_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Before"}},{"name":"include_background","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Background"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditPageView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/visits":{"get":{"tags":["nodes"],"summary":"Hosted app page loads over the last 24 hours","operationId":"visits_api_visits_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppVisitsView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/github/install":{"get":{"tags":["github"],"summary":"Send the signed-in person to GitHub's install page","description":"Seal a state and send the browser to GitHub.\n\n``target`` is the console's \"Configure on GitHub\" link (task 15\nG1.2). It changes only the destination: the state is sealed exactly\nas it is without one, so the return trip is the same callback under\nthe same session rule, and the ``update`` branch that already exists\ndoes the re-mirroring. This route — never a link the console\nassembles — is why configuring by hand no longer ends in\n``state_invalid``: a hand-typed github.com configure page carries no\nstate, and ours does.\n\nAn installation the session's account does not hold answers 404 with\nthe same body an id that never existed gets. There is no oracle here\non purpose: installation ids are small global integers, and a\ndifferent answer for \"somebody else's\" would turn this route into a\nway to ask whether an arbitrary id is installed anywhere.","operationId":"start_install_github_install_get","parameters":[{"name":"target","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":9223372036854775807,"minimum":1},{"type":"null"}],"title":"Target"}},{"name":"flow","in":"query","required":false,"schema":{"anyOf":[{"const":"new","type":"string"},{"type":"null"}],"title":"Flow"}},{"name":"node","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Node"}},{"name":"project","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":20,"pattern":"^[A-Za-z0-9_-]+$"},{"type":"null"}],"title":"Project"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/github/callback":{"get":{"tags":["github"],"summary":"The App's setup URL: bind the installation to the signed-in account","operationId":"install_callback_github_callback_get","parameters":[{"name":"state","in":"query","required":false,"schema":{"type":"string","default":"","title":"State"}},{"name":"installation_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Installation Id"}},{"name":"setup_action","in":"query","required":false,"schema":{"type":"string","default":"","title":"Setup Action"}},{"name":"code","in":"query","required":false,"schema":{"type":"string","default":"","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/github/webhook":{"post":{"tags":["github"],"summary":"Accept one signed GitHub App delivery","description":"Verify, record, acknowledge — in that order, and nothing else.\n\nNo session and no CSRF: the request carries no cookie, so the\nmiddleware passes it through, and the signature is the entire\nadmission decision. The side effects happen off this request, from\nthe row, so GitHub's ten-second delivery window never contains a\ntunnel round trip.","operationId":"webhook_github_webhook_post","responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubWebhookAck"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"Content Too Large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/github":{"get":{"tags":["github"],"summary":"The GitHub App installations bound to this account","operationId":"github_overview_api_github_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubView"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/github/installations/{installation_id}/repos":{"get":{"tags":["github"],"summary":"The repositories one installation grants","operationId":"installation_repos_api_github_installations__installation_id__repos_get","parameters":[{"name":"installation_id","in":"path","required":true,"schema":{"type":"integer","title":"Installation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubInstallationReposView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/github/installations/{installation_id}/refresh":{"post":{"tags":["github"],"summary":"Re-read the installation's repositories from the App","description":"Replace the mirror with what GitHub reports now.\n\nA removed installation is not re-read: GitHub would 404 it, and the\nanswer is already known. An installation GitHub no longer knows is\nmarked removed here, so the page stops offering the button.","operationId":"refresh_installation_api_github_installations__installation_id__refresh_post","parameters":[{"name":"installation_id","in":"path","required":true,"schema":{"type":"integer","title":"Installation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubInstallationReposView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/github/nodes/{node_id}/repos":{"get":{"tags":["github"],"summary":"The repositories an owned node may receive a token for","operationId":"node_repos_api_github_nodes__node_id__repos_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubNodeReposView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["github"],"summary":"Replace the repositories an owned node may receive a token for","description":"Replace the picked set and wake the pusher.\n\nEvery id must be a repository of one of this account's live\ninstallations: an id from another account, an unknown one, or one of\nan uninstalled App is a 422 that names nothing (the ids are not this\ncaller's to enumerate). The write is a replace, not a merge, so the\nconsole's checkbox list is the whole truth. The pusher is woken after\nthe commit; a narrowing reaches the node and revokes the old token\nfrom there (D-GH-C4), never from this request.","operationId":"set_node_repos_api_github_nodes__node_id__repos_put","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubNodeReposRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubNodeReposView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"}}}},"/api/github/nodes/{node_id}/deploy":{"post":{"tags":["github"],"summary":"Deploy a granted GitHub repository on an owned node","description":"Picker row → token push → ``deploy_git`` → ``auto_deploy`` (D-GH-C6).\n\nThe order is the load-bearing part. The repository is added to the\nnode's picker first, in its own committed transaction, so the token\nthe pusher mints next covers it; the push is *synchronous* — the\nroute waits for the daemon to hold the token — because the clone\nthat follows resolves ``${github.installation}`` on the node and\nwould fail with \"no token for this repository\" a moment earlier. Only\nthen is the daemon asked to clone and register the app, with the\nclone URL built here from the granted ``owner/name`` (never from the\nrequest's spelling) and the reference name rather than a credential.\n``auto_deploy`` is written last, and a failure there is reported in\nthe view rather than raised: the app exists by then.\n\nA daemon without the token route (pre-P33) cannot resolve the\nreference at all, so that is a 409 naming the upgrade rather than a\nclone that fails with a hint about secrets. Inactive or suspended\naccounts are refused with ``ENTITLEMENT_REQUIRED`` (403).","operationId":"deploy_from_github_api_github_nodes__node_id__deploy_post","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubDeployRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubDeployView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/github/nodes/{node_id}/deploy/preview":{"post":{"tags":["github"],"summary":"Inspect a granted repository's build on an owned node","description":"Prepares scoped repository access and runs the node's build inspection. Does not create a service or write its auto-deploy setting.","operationId":"deploy_from_github_api_github_nodes__node_id__deploy_preview_post","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubDeployRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubBuildPreviewView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Bad Gateway"}}}},"/api/github/nodes/{node_id}/deploy/env-check":{"get":{"tags":["github"],"summary":"Which keys a repository's .env.example names that the node lacks","description":"Read ``.env.example`` from the repo, compare it with the node's keys.\n\nAdvisory and read-only, in both directions: it is never audited, it\nhas no entitlement gate, and every way it can fail — no manifest, an\noffline node, a silent one — is a ``checked: false`` with a reason\nrather than an error, because a check that refuses harder than the\ndeploy it precedes would stop deploys that work.\n\nNames only. The manifest's values are dropped inside\n:func:`~nerdit_cloud.github.envkeys.parse_env_keys` and the daemon\nanswers with names by contract, so no secret and no line of somebody\nelse's repository reaches this response or a log record.","operationId":"deploy_env_check_api_github_nodes__node_id__deploy_env_check_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}},{"name":"repo","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":200,"title":"Repo"}},{"name":"name","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":63},{"type":"null"}],"title":"Name"}},{"name":"ref","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Ref"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployEnvCheckView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unprocessable Content"}}}},"/api/github/nodes/{node_id}/sources":{"get":{"tags":["github"],"summary":"The git-sourced apps of an owned node and how their last push fared","description":"Join the node's own source list with what this cloud did about it.\n\nThe node is the authority on its sources (one bounded read of\n``GET /api/services``, empty when offline or unreadable — the card\nsays \"offline\" rather than failing); the nudge records come from the\npoller's memory and the status receipts from ``github_status_posts``.\nAn app known only from those (the node is offline, or removed it) is\nstill listed, with no provenance.","operationId":"node_sources_api_github_nodes__node_id__sources_get","parameters":[{"name":"node_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Node Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitHubNodeSourcesView"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"Not Found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AccessAppRef":{"properties":{"rule_id":{"type":"string","format":"uuid","title":"Rule Id"},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_name":{"type":"string","title":"Node Name"},"service_name":{"type":"string","title":"Service Name"}},"additionalProperties":false,"type":"object","required":["rule_id","node_id","node_name","service_name"],"title":"AccessAppRef","description":"An app a person is let into directly, by the rule that does it."},"AccessAppView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_name":{"type":"string","title":"Node Name"},"service_name":{"type":"string","title":"Service Name"},"rules":{"items":{"$ref":"#/components/schemas/AccessRuleView"},"type":"array","title":"Rules"}},"additionalProperties":false,"type":"object","required":["node_id","node_name","service_name","rules"],"title":"AccessAppView","description":"One app someone else may open, and its rules. No lock: no machine is asked."},"AccessAppsView":{"properties":{"apps":{"items":{"$ref":"#/components/schemas/AccessAppView"},"type":"array","title":"Apps"}},"additionalProperties":false,"type":"object","required":["apps"],"title":"AccessAppsView"},"AccessGroupRef":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"}},"additionalProperties":false,"type":"object","required":["id","name"],"title":"AccessGroupRef"},"AccessGroupRequest":{"properties":{"name":{"type":"string","maxLength":320,"title":"Name"}},"additionalProperties":false,"type":"object","required":["name"],"title":"AccessGroupRequest","description":"A group's name, as the owner types it."},"AccessGroupView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"members":{"items":{"$ref":"#/components/schemas/AccessMemberView"},"type":"array","title":"Members"},"apps":{"type":"integer","title":"Apps"}},"additionalProperties":false,"type":"object","required":["id","name","created_at","members","apps"],"title":"AccessGroupView","description":"One group, its people, and how many apps it is attached to."},"AccessGroupsView":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/AccessGroupView"},"type":"array","title":"Groups"}},"additionalProperties":false,"type":"object","required":["groups"],"title":"AccessGroupsView","description":"The account's groups, oldest first."},"AccessLogEntry":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_name":{"type":"string","title":"Node Name"},"service_name":{"type":"string","title":"Service Name"},"email":{"type":"string","title":"Email"},"provider":{"type":"string","enum":["dev","google","email"],"title":"Provider"},"rule_id":{"type":"string","format":"uuid","title":"Rule Id"},"rule_kind":{"type":"string","enum":["email","google_domain","group"],"title":"Rule Kind"},"rule_value":{"type":"string","title":"Rule Value"},"group_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_seen_at":{"type":"string","format":"date-time","title":"Last Seen At"},"state":{"type":"string","enum":["active","ended","expired"],"title":"State"},"ended_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Ended At"}},"additionalProperties":false,"type":"object","required":["id","node_id","node_name","service_name","email","provider","rule_id","rule_kind","rule_value","created_at","last_seen_at","state","ended_at"],"title":"AccessLogEntry","description":"One visitor session (lot 7, D7). ``id`` is the session's.\n\nNever the subject, the token hash, the host or the deadlines."},"AccessLogView":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/AccessLogEntry"},"type":"array","title":"Entries"},"next_before":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Next Before"}},"additionalProperties":false,"type":"object","required":["entries","next_before"],"title":"AccessLogView","description":"One page, newest first; ``next_before`` pages on (as ``/api/audit``)."},"AccessMemberRequest":{"properties":{"kind":{"type":"string","enum":["email","google_domain"],"title":"Kind"},"value":{"type":"string","maxLength":320,"minLength":1,"title":"Value"}},"additionalProperties":false,"type":"object","required":["kind","value"],"title":"AccessMemberRequest","description":"One address or Google Workspace domain, in a group or everywhere."},"AccessMemberView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"kind":{"type":"string","enum":["email","google_domain"],"title":"Kind"},"value":{"type":"string","title":"Value"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"additionalProperties":false,"type":"object","required":["id","kind","value","created_at"],"title":"AccessMemberView","description":"One person in a group. ``value`` is lower-case, as it is compared."},"AccessPeopleView":{"properties":{"people":{"items":{"$ref":"#/components/schemas/AccessPersonView"},"type":"array","title":"People"}},"additionalProperties":false,"type":"object","required":["people"],"title":"AccessPeopleView","description":"Everyone let in, latest visit first."},"AccessPersonView":{"properties":{"kind":{"type":"string","enum":["email","google_domain"],"title":"Kind"},"value":{"type":"string","title":"Value"},"apps":{"items":{"$ref":"#/components/schemas/AccessAppRef"},"type":"array","title":"Apps"},"groups":{"items":{"$ref":"#/components/schemas/AccessGroupRef"},"type":"array","title":"Groups"},"domains":{"items":{"type":"string"},"type":"array","title":"Domains"},"last_seen_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Seen At"}},"additionalProperties":false,"type":"object","required":["kind","value","apps","groups","domains","last_seen_at"],"title":"AccessPersonView","description":"One address or domain let in, and every way it is."},"AccessRuleAdded":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"kind":{"type":"string","enum":["email","google_domain","group"],"title":"Kind"},"value":{"type":"string","title":"Value"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"additionalProperties":false,"type":"object","required":["id","kind","value","created_at"],"title":"AccessRuleAdded","description":"The rule an add answers with. ``value`` is lower-case, as it is compared.\n\nNo session count: the add never computes one, and a repeat names a rule\nthat may already have visitors. The list states it."},"AccessRuleRequest":{"properties":{"kind":{"type":"string","enum":["email","google_domain","group"],"title":"Kind"},"value":{"type":"string","maxLength":320,"minLength":1,"title":"Value"},"invite":{"type":"boolean","title":"Invite","default":false}},"additionalProperties":false,"type":"object","required":["kind","value"],"title":"AccessRuleRequest","description":"One rule: an address, a Google Workspace domain, or a group by its id."},"AccessRuleView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"kind":{"type":"string","enum":["email","google_domain","group"],"title":"Kind"},"value":{"type":"string","title":"Value"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"active_sessions":{"type":"integer","title":"Active Sessions"},"group_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group Name"},"group_people":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Group People"}},"additionalProperties":false,"type":"object","required":["id","kind","value","created_at","active_sessions"],"title":"AccessRuleView","description":"One active rule, as the list shows it."},"AccessRulesView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"service_name":{"type":"string","title":"Service Name"},"rules":{"items":{"$ref":"#/components/schemas/AccessRuleView"},"type":"array","title":"Rules"},"invitations":{"type":"boolean","title":"Invitations"},"lock":{"anyOf":[{"type":"string","enum":["locked","pending","unknown","off"]},{"type":"null"}],"title":"Lock"},"agent_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Url"},"identity_header":{"type":"boolean","title":"Identity Header","default":false}},"additionalProperties":false,"type":"object","required":["node_id","service_name","rules","invitations","lock"],"title":"AccessRulesView","description":"Who besides the owner may open one private hosted app.\n\nRules apply to a ``private`` share only: a ``public`` one is served to\neverybody whatever its rules say (ADR-W9, \"public wins\")."},"AccountAuthorizeContextView":{"properties":{"client_id":{"type":"string","title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"self_registered":{"type":"boolean","title":"Self Registered"},"role":{"type":"string","const":"submitter","title":"Role"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"redirect_uri":{"type":"string","title":"Redirect Uri"},"resource_kind":{"type":"string","const":"account","title":"Resource Kind"},"nodes":{"items":{"$ref":"#/components/schemas/AccountConsentNodeView"},"type":"array","title":"Nodes"},"scope_kind":{"type":"string","enum":["nodes","projects"],"title":"Scope Kind"},"projects":{"items":{"$ref":"#/components/schemas/AccountConsentProjectView"},"type":"array","title":"Projects"},"already_consented":{"type":"boolean","title":"Already Consented"}},"additionalProperties":false,"type":"object","required":["client_id","client_name","self_registered","role","scopes","redirect_uri","resource_kind","nodes","scope_kind","projects","already_consented"],"title":"AccountAuthorizeContextView","description":"A consent question for the stable account MCP gateway."},"AccountConsentNodeView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"},"display_name":{"type":"string","title":"Display Name"},"selected":{"type":"boolean","title":"Selected"}},"additionalProperties":false,"type":"object","required":["id","slug","display_name","selected"],"title":"AccountConsentNodeView","description":"One selectable account-gateway destination and its initial state."},"AccountConsentProjectView":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_name":{"type":"string","title":"Node Name"},"selected":{"type":"boolean","title":"Selected"}},"additionalProperties":false,"type":"object","required":["id","name","node_id","node_name","selected"],"title":"AccountConsentProjectView"},"AccountDeletionView":{"properties":{"revoked_grants":{"type":"integer","title":"Revoked Grants"},"revoked_families":{"type":"integer","title":"Revoked Families"},"revoked_proxy_sessions":{"type":"integer","title":"Revoked Proxy Sessions"},"closed_streams":{"type":"integer","title":"Closed Streams"},"web_sessions_revoked":{"type":"integer","title":"Web Sessions Revoked"},"billing_cancellation":{"type":"string","enum":["none","pending","canceled"],"title":"Billing Cancellation","default":"none"},"github_uninstalls_failed":{"type":"integer","title":"Github Uninstalls Failed","default":0},"nodes_revoked":{"type":"integer","title":"Nodes Revoked","default":0},"identity_erasure":{"type":"string","enum":["not_applicable","pending","erased"],"title":"Identity Erasure","default":"not_applicable"},"marketing_contact":{"type":"string","enum":["not_applicable","pending","forgotten"],"title":"Marketing Contact","default":"not_applicable"},"managed_machine":{"type":"string","enum":["none","pending"],"title":"Managed Machine","default":"none"}},"additionalProperties":false,"type":"object","required":["revoked_grants","revoked_families","revoked_proxy_sessions","closed_streams","web_sessions_revoked"],"title":"AccountDeletionView","description":"Everything a deletion ended, counted per kind.\n\nCounts rather than a bare acknowledgement because they are the only\nconfirmation the person will ever get: the session this answered on is\nalready dead, so there is no page left to come back and check."},"AccountGrantAccessRequest":{"properties":{"scope_kind":{"type":"string","enum":["nodes","projects"],"title":"Scope Kind"},"node_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":1000},{"type":"null"}],"title":"Node Ids"},"project_ids":{"anyOf":[{"items":{"type":"string","maxLength":20,"minLength":20,"pattern":"^prj_[a-z2-7]{16}$"},"type":"array","maxItems":1000},{"type":"null"}],"title":"Project Ids"}},"additionalProperties":false,"type":"object","required":["scope_kind"],"title":"AccountGrantAccessRequest"},"AccountGrantAccessView":{"properties":{"closed_streams":{"type":"integer","title":"Closed Streams"},"revoked_families":{"type":"integer","title":"Revoked Families"},"reconnect_required":{"type":"boolean","title":"Reconnect Required"}},"additionalProperties":false,"type":"object","required":["closed_streams","revoked_families","reconnect_required"],"title":"AccountGrantAccessView"},"AccountGrantNodeView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"},"display_name":{"type":"string","title":"Display Name"}},"additionalProperties":false,"type":"object","required":["id","slug","display_name"],"title":"AccountGrantNodeView"},"AccountGrantNodesRequest":{"properties":{"node_ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":1000,"title":"Node Ids"}},"additionalProperties":false,"type":"object","required":["node_ids"],"title":"AccountGrantNodesRequest"},"AccountGrantNodesView":{"properties":{"closed_streams":{"type":"integer","title":"Closed Streams"},"revoked_families":{"type":"integer","title":"Revoked Families"},"reconnect_required":{"type":"boolean","title":"Reconnect Required"}},"additionalProperties":false,"type":"object","required":["closed_streams","revoked_families","reconnect_required"],"title":"AccountGrantNodesView"},"AccountGrantProjectView":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_name":{"type":"string","title":"Node Name"}},"additionalProperties":false,"type":"object","required":["id","name","node_id","node_name"],"title":"AccountGrantProjectView"},"AccountGrantView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"client_revoked":{"type":"boolean","title":"Client Revoked"},"self_registered":{"type":"boolean","title":"Self Registered"},"redirect_uris":{"items":{"type":"string"},"type":"array","title":"Redirect Uris"},"resource":{"type":"string","title":"Resource"},"role":{"type":"string","const":"submitter","title":"Role"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"granted_at":{"type":"string","format":"date-time","title":"Granted At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"scope_kind":{"type":"string","enum":["nodes","projects"],"title":"Scope Kind"},"nodes":{"items":{"$ref":"#/components/schemas/AccountGrantNodeView"},"type":"array","title":"Nodes"},"projects":{"items":{"$ref":"#/components/schemas/AccountGrantProjectView"},"type":"array","title":"Projects"},"families":{"items":{"$ref":"#/components/schemas/FamilyView"},"type":"array","title":"Families"}},"additionalProperties":false,"type":"object","required":["id","client_id","client_name","client_revoked","self_registered","redirect_uris","resource","role","scopes","granted_at","updated_at","scope_kind","nodes","projects","families"],"title":"AccountGrantView"},"AccountRevocationView":{"properties":{"revoked_grants":{"type":"integer","title":"Revoked Grants"},"revoked_families":{"type":"integer","title":"Revoked Families"},"revoked_proxy_sessions":{"type":"integer","title":"Revoked Proxy Sessions"},"revoked_visitor_sessions":{"type":"integer","title":"Revoked Visitor Sessions"},"closed_streams":{"type":"integer","title":"Closed Streams"}},"additionalProperties":false,"type":"object","required":["revoked_grants","revoked_families","revoked_proxy_sessions","revoked_visitor_sessions","closed_streams"],"title":"AccountRevocationView","description":"Everything \"revoke all remote access\" reached, counted per kind."},"AppTemplateView":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"}},"additionalProperties":false,"type":"object","required":["id","name","description"],"title":"AppTemplateView","description":"One starter app the machine can deploy. Only what the picker renders."},"AppVisitView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_name":{"type":"string","title":"Node Name"},"service_name":{"type":"string","title":"Service Name"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"status_code":{"type":"integer","title":"Status Code"},"audience":{"type":"string","enum":["owner","visitor","public"],"title":"Audience"}},"type":"object","required":["id","node_id","node_name","service_name","occurred_at","status_code","audience"],"title":"AppVisitView"},"AppVisitsView":{"properties":{"since":{"type":"string","format":"date-time","title":"Since"},"until":{"type":"string","format":"date-time","title":"Until"},"total":{"type":"integer","title":"Total"},"buckets":{"items":{"$ref":"#/components/schemas/VisitBucket"},"type":"array","title":"Buckets"},"recent":{"items":{"$ref":"#/components/schemas/AppVisitView"},"type":"array","title":"Recent"}},"type":"object","required":["since","until","total","buckets","recent"],"title":"AppVisitsView"},"AuditEntryView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"action":{"type":"string","title":"Action"},"actor_type":{"type":"string","title":"Actor Type"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"target_type":{"type":"string","title":"Target Type"},"target_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Id"},"result":{"type":"string","title":"Result"},"request_id":{"type":"string","title":"Request Id"},"metadata":{"additionalProperties":{"$ref":"#/components/schemas/JsonValue"},"type":"object","title":"Metadata"}},"additionalProperties":false,"type":"object","required":["id","occurred_at","action","actor_type","target_type","target_id","result","request_id","metadata"],"title":"AuditEntryView","description":"One recorded mutation, as the person it concerns is shown it."},"AuditPageView":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/AuditEntryView"},"type":"array","title":"Entries"},"next_before":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Next Before"}},"additionalProperties":false,"type":"object","required":["entries","next_before"],"title":"AuditPageView","description":"One page of the caller's trail, newest first, and where to continue.\n\n``next_before`` is present only when there is genuinely more: the query\nreads one row past the page to find out, so following the cursor never\nlands on an empty page that looks like the end arriving twice."},"AuthorizeContextView":{"oneOf":[{"$ref":"#/components/schemas/NodeAuthorizeContextView"},{"$ref":"#/components/schemas/AccountAuthorizeContextView"}],"title":"AuthorizeContextView","description":"Discriminated consent context returned under one stable endpoint.","discriminator":{"propertyName":"resource_kind","mapping":{"account":"#/components/schemas/AccountAuthorizeContextView","node":"#/components/schemas/NodeAuthorizeContextView"}}},"AuthorizeDecisionRequest":{"properties":{"rid":{"type":"string","maxLength":512,"minLength":1,"title":"Rid"},"decision":{"type":"string","enum":["approve","deny"],"title":"Decision"},"node_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":1000},{"type":"null"}],"title":"Node Ids"},"scope_kind":{"anyOf":[{"type":"string","enum":["nodes","projects"]},{"type":"null"}],"title":"Scope Kind"},"project_ids":{"anyOf":[{"items":{"type":"string","maxLength":20,"minLength":20,"pattern":"^prj_[a-z2-7]{16}$"},"type":"array","maxItems":1000},{"type":"null"}],"title":"Project Ids"}},"additionalProperties":false,"type":"object","required":["rid","decision"],"title":"AuthorizeDecisionRequest","description":"The person's answer to one pending authorize transaction."},"AuthorizeDecisionView":{"properties":{"redirect_to":{"type":"string","title":"Redirect To"}},"additionalProperties":false,"type":"object","required":["redirect_to"],"title":"AuthorizeDecisionView","description":"Where the browser goes next, built only from the registered URI."},"BillingView":{"properties":{"plan":{"type":"string","title":"Plan"},"status":{"type":"string","title":"Status"},"hosted_public_entitled":{"type":"boolean","title":"Hosted Public Entitled"},"current_period_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Current Period End"},"cancel_at_period_end":{"type":"boolean","title":"Cancel At Period End"},"trial_ends_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Trial Ends At"},"pro_trial_days":{"type":"integer","title":"Pro Trial Days"},"has_billing_account":{"type":"boolean","title":"Has Billing Account"},"pro_available":{"type":"boolean","title":"Pro Available"}},"additionalProperties":false,"type":"object","required":["plan","status","hosted_public_entitled","current_period_end","cancel_at_period_end","trial_ends_at","pro_trial_days","has_billing_account","pro_available"],"title":"BillingView","description":"The whole of what ``/billing`` renders, straight off the projection."},"CertificateView":{"properties":{"requested":{"type":"boolean","title":"Requested"},"cert_state":{"type":"string","enum":["internal","disabled","pending","issued","expired"],"title":"Cert State"},"acme_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Acme Enabled"},"http_reachable":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Http Reachable"}},"additionalProperties":false,"type":"object","required":["requested","cert_state","acme_enabled","http_reachable"],"title":"CertificateView","description":"The ACME preconditions, each from the source that actually knows it.\n\n``requested`` and ``cert_state`` come from the node's own row.\n``acme_enabled`` comes from ``GET /api/capabilities`` — see\n:data:`CAPABILITIES_PATH` for why the row cannot answer it — and is ``None``\nwhen that read could not be made or could not be read, which is a fault on\none precondition and never a refusal of the whole check.\n``http_reachable`` is the HTTP-01 precondition, and ``None`` when nothing\nwas probed."},"CheckoutRequest":{"properties":{"plan":{"type":"string","const":"pro","title":"Plan","default":"pro"}},"additionalProperties":false,"type":"object","title":"CheckoutRequest","description":"Which tier the browser is asking to buy; a body-less POST buys Pro."},"CheckoutSessionView":{"properties":{"checkout_url":{"type":"string","title":"Checkout Url"}},"additionalProperties":false,"type":"object","required":["checkout_url"],"title":"CheckoutSessionView","description":"Where to send the browser. Holding it grants nothing (W-D10 v2)."},"ClientView":{"properties":{"client_id":{"type":"string","title":"Client Id"},"client_name":{"type":"string","title":"Client Name"}},"additionalProperties":false,"type":"object","required":["client_id","client_name"],"title":"ClientView","description":"One client this deployment would accept an authorization from.\n\nDeployment-global rather than per-account, and deliberately so: the list is\noperator-curated and its identifiers are pasted into Claude by hand, so a\nclient with no grant from this person still belongs on the page — it is\nwhat they are about to connect, and the page is where they check that the\nidentifier they were given is one this server knows.\n\nWhich is why a client an operator has taken out of service is absent here\neven when a grant above still names it: this collection answers \"would an\nauthorization for this identifier be accepted\", and for that one the answer\nis no. The grant is the thing that survived, and it is listed where the\ngrants are."},"CloudDomainRecord":{"properties":{"type":{"type":"string","const":"CNAME","title":"Type","default":"CNAME"},"name":{"type":"string","title":"Name"},"target":{"type":"string","title":"Target"}},"additionalProperties":false,"type":"object","required":["name","target"],"title":"CloudDomainRecord"},"CloudDomainView":{"properties":{"host":{"type":"string","title":"Host"},"status":{"type":"string","enum":["awaiting_dns","pending","active","error"],"title":"Status"},"record":{"$ref":"#/components/schemas/CloudDomainRecord"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"verified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Verified At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"additionalProperties":false,"type":"object","required":["host","status","record","created_at","verified_at","expires_at"],"title":"CloudDomainView"},"CloudDomainWrite":{"properties":{"host":{"type":"string","maxLength":253,"minLength":1,"title":"Host"}},"additionalProperties":false,"type":"object","required":["host"],"title":"CloudDomainWrite"},"ConnectorNodeView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"slug":{"type":"string","title":"Slug"},"display_name":{"type":"string","title":"Display Name"},"online":{"type":"boolean","title":"Online"},"mcp_url":{"type":"string","title":"Mcp Url"}},"additionalProperties":false,"type":"object","required":["node_id","slug","display_name","online","mcp_url"],"title":"ConnectorNodeView","description":"One owned node, its live state, and its advanced direct URL."},"ConnectorView":{"properties":{"entitled":{"type":"boolean","title":"Entitled"},"account_connector":{"type":"boolean","title":"Account Connector"},"mcp_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mcp Url"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"},"node":{"anyOf":[{"$ref":"#/components/schemas/ConnectorNodeView"},{"type":"null"}]},"advanced_node_connectors":{"items":{"$ref":"#/components/schemas/ConnectorNodeView"},"type":"array","title":"Advanced Node Connectors"}},"additionalProperties":false,"type":"object","required":["entitled","account_connector","mcp_url","client_id","client_name","node","advanced_node_connectors"],"title":"ConnectorView","description":"What a person has to type into Claude, read from this deployment.\n\nEvery field is nullable for the same reason: the ``/claude`` page must\nnever print a plausible-looking instruction that this server would refuse.\nNo registered client under\n:attr:`~nerdit_cloud.config.Settings.mcp_connector_client_id` means the\nidentifier would be rejected at ``/oauth/authorize``; no Remote Link\nentitlement means the consent screen would refuse the grant no matter what\nwas pasted. In both cases the page says so instead of showing a value.\n\nThe account gateway URL does not wait for a node: an entitled account gets\n:attr:`mcp_url` with the gateway on even before its first machine, so the\nconsole can put \"connect your agent\" first (2026-09-29). The consent\nscreen still refuses with 409 until a machine exists. Only a deployment\nwith the gateway dark needs a node, because the URL it recommends is that\nnode's own.\n\n:attr:`account_connector` is the same rule applied to *which* URL is\nrecommended, and it is why the recommendation is a server answer rather\nthan a page constant: ``/mcp`` answers 503 while\n:attr:`~nerdit_cloud.config.Settings.account_mcp_gateway_enabled` is off,\nso a deployment with the gateway dark recommends the direct node connector\nit does serve."},"ConsentNodeView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"},"display_name":{"type":"string","title":"Display Name"}},"additionalProperties":false,"type":"object","required":["id","slug","display_name"],"title":"ConsentNodeView","description":"The node a grant would apply to, named the way the console names it."},"DeployEnvCheckView":{"properties":{"checked":{"type":"boolean","title":"Checked"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"needed":{"items":{"type":"string"},"type":"array","title":"Needed"},"missing":{"items":{"type":"string"},"type":"array","title":"Missing"}},"additionalProperties":false,"type":"object","required":["checked","reason","needed","missing"],"title":"DeployEnvCheckView","description":"Which keys the repository's ``.env.example`` names that the node lacks.\n\n``checked`` is whether the comparison happened at all; when it did not,\n``reason`` says why and both lists are empty. The vocabulary is\n``no_manifest`` (the repository has no ``.env.example`` — the common\ncase, and not an error), ``node_offline``, ``node_silent`` (the node did\nnot answer) and ``shared_unavailable`` (the daemon holds a legacy\nservice literally named ``shared``), plus ``github_silent``.\n\n``needed`` is the manifest's key *names*, in the order it lists them, and\n``missing`` is the ones neither the shared set nor the service's own set\nholds. Values are never read out of the manifest, so nothing here can\ncarry one."},"DeviceApprovalGrant":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_slug":{"type":"string","title":"Node Slug"}},"additionalProperties":false,"type":"object","required":["node_id","node_slug"],"title":"DeviceApprovalGrant","description":"What an approval produced: the node the machine will come up as."},"DeviceApprovalRequest":{"properties":{"user_code":{"type":"string","maxLength":64,"minLength":1,"title":"User Code"}},"additionalProperties":false,"type":"object","required":["user_code"],"title":"DeviceApprovalRequest","description":"The one thing all three routes take, and the only place it may travel."},"DeviceApprovalView":{"properties":{"credential_fingerprint":{"type":"string","title":"Credential Fingerprint"},"credential_fingerprint_preview":{"type":"string","title":"Credential Fingerprint Preview"},"requested_at":{"type":"string","format":"date-time","title":"Requested At"},"requested_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requested Ip"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"reported":{"$ref":"#/components/schemas/ReportedMachine"}},"additionalProperties":false,"type":"object","required":["credential_fingerprint","credential_fingerprint_preview","requested_at","requested_ip","expires_at","reported"],"title":"DeviceApprovalView","description":"Everything the approval screen renders, and nothing that identifies it.\n\nNo row id, no node id, no account: this is answered to whoever presents the\ncode, and it should tell a wrong guess as little as the 404 does."},"DeviceLinkRequest":{"properties":{"credential_reference":{"type":"string","maxLength":255,"minLength":1,"title":"Credential Reference"},"hostname_hint":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Hostname Hint"},"daemon_version":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Daemon Version"},"os":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Os"}},"additionalProperties":false,"type":"object","required":["credential_reference"],"title":"DeviceLinkRequest","description":"What a daemon says about itself when it asks to be linked.\n\nOnly ``credential_reference`` is load-bearing: it is pinned to the row here\nand every later step — the fingerprint the console renders, the node the\napproval adopts, the poll's constant-time comparison — is bound to *that*\nvalue (D-X16-O22), never to whatever presents itself later.\n\nThe other three are display strings and are treated as hostile\n(D-X16-O23). They are bounded above; nothing downstream may render them\nwithout saying, in the copy, that the requesting machine reported them and\nnobody verified them."},"DeviceLinkResponse":{"properties":{"device_code":{"type":"string","title":"Device Code"},"user_code":{"type":"string","title":"User Code"},"verification_uri":{"type":"string","title":"Verification Uri"},"verification_uri_complete":{"type":"string","title":"Verification Uri Complete"},"credential_fingerprint":{"type":"string","title":"Credential Fingerprint"},"expires_in":{"type":"integer","title":"Expires In"},"interval":{"type":"integer","title":"Interval"}},"additionalProperties":false,"type":"object","required":["device_code","user_code","verification_uri","verification_uri_complete","credential_fingerprint","expires_in","interval"],"title":"DeviceLinkResponse","description":"The two artifacts, where to take one of them, and how long they last.\n\n``device_code`` is the daemon's poll credential and is never displayed;\n``user_code`` is what the terminal prints for a human to carry. Both leave\nthis process exactly once, in this response, and neither is stored: the row\nkeeps only ``sha256_hex`` of each (D-X16-O3).\n\n``verification_uri_complete`` carries the user code in a **fragment**\n(D-X16-O24), so the one-click link cannot deposit the secret in an access\nlog, a CDN log, a ``Referer`` header or a shared-link history the way\n``?user_code=`` would. ``credential_fingerprint`` is the same eight-hex\nprefix the console will show beside the approval, so an operator standing at\nthe terminal can check that the screen is describing *this* machine\n(D-X16-O4 — an aid for the operator who has the terminal, never a defence\nagainst a phished stranger)."},"DevicePollPending":{"properties":{"status":{"type":"string","enum":["authorization_pending","slow_down"],"title":"Status"},"interval":{"type":"integer","title":"Interval"}},"additionalProperties":false,"type":"object","required":["status","interval"],"title":"DevicePollPending","description":"The 202 body: nobody has answered, and here is when to ask again.\n\nTwo statuses, both RFC 8628's. ``authorization_pending`` is the honest\nanswer to a polite poll; ``slow_down`` is what a daemon polling faster than\nthe interval it was given gets instead, with the longer interval it should\nadopt. Neither says anything about the request beyond \"not settled\", which\nis what keeps a fast-polling stranger from learning that a code exists."},"DevicePollRequest":{"properties":{"device_code":{"type":"string","maxLength":128,"minLength":1,"title":"Device Code"},"credential_reference":{"type":"string","maxLength":255,"minLength":1,"title":"Credential Reference"}},"additionalProperties":false,"type":"object","required":["device_code","credential_reference"],"title":"DevicePollRequest","description":"A daemon asking whether a human has answered yet.\n\nThe credential travels on every poll because the comparison against the\nrow's pinned value is the *first* thing the policy layer does with the row\n(D-X16-O22): everything after it reveals state — approved, denied, expired —\nand a caller who cannot present the credential the request was minted with\nhas no business learning any of it."},"DomainBindRequest":{"properties":{"acme":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Acme"}},"additionalProperties":false,"type":"object","title":"DomainBindRequest","description":"The body beside the path id: the one decision that is not the identity."},"DomainBoundView":{"properties":{"domain":{"type":"string","title":"Domain"},"acme":{"type":"boolean","title":"Acme"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"url":{"type":"string","title":"Url"},"state":{"type":"string","enum":["ready","withheld"],"title":"State"},"cert_state":{"type":"string","enum":["internal","disabled","pending","issued","expired"],"title":"Cert State"},"cert_not_after":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Cert Not After"},"created":{"type":"boolean","title":"Created"}},"type":"object","required":["domain","acme","created_at","url","state","cert_state","created"],"title":"DomainBoundView","description":"``PUT`` answer: the row as it now stands, plus whether this call made it."},"DomainCheckView":{"properties":{"service_name":{"type":"string","title":"Service Name"},"domain":{"type":"string","title":"Domain"},"checked_at":{"type":"string","format":"date-time","title":"Checked At"},"resolved":{"$ref":"#/components/schemas/ResolvedView"},"observed":{"$ref":"#/components/schemas/ObservedView"},"verdict":{"type":"string","enum":["points_here","points_elsewhere","unresolved","unknown"],"title":"Verdict"},"reachability":{"$ref":"#/components/schemas/ReachabilityView"},"certificate":{"$ref":"#/components/schemas/CertificateView"}},"additionalProperties":false,"type":"object","required":["service_name","domain","checked_at","resolved","observed","verdict","reachability","certificate"],"title":"DomainCheckView","description":"One live look at one bound name. Nothing here is remembered.\n\n``checked_at`` is on the view rather than implied by the request because the\nconsole renders \"checked N seconds ago\": a finding with no age reads as a\nstanding fact about a name, and none of these are."},"DomainReleasedView":{"properties":{"service_name":{"type":"string","title":"Service Name"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"removed":{"type":"boolean","title":"Removed"}},"additionalProperties":false,"type":"object","required":["service_name","domain","removed"],"title":"DomainReleasedView","description":"``DELETE`` answer. Idempotent: ``removed: false`` is a normal outcome."},"DomainRow":{"properties":{"domain":{"type":"string","title":"Domain"},"acme":{"type":"boolean","title":"Acme"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"url":{"type":"string","title":"Url"},"state":{"type":"string","enum":["ready","withheld"],"title":"State"},"cert_state":{"type":"string","enum":["internal","disabled","pending","issued","expired"],"title":"Cert State"},"cert_not_after":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Cert Not After"}},"type":"object","required":["domain","acme","created_at","url","state","cert_state"],"title":"DomainRow","description":"One bound name, exactly as the node reports it.\n\n``extra=\"ignore\"`` is drop-not-default: a field this console does not know\nis dropped and never invented. ``url``, ``state`` and the two certificate\nfields are computed by the node against its live proxy on every read — the\nrow records the binding, the proxy decides whether it answers right now — so\nthey are reported, never remembered (D-NC10)."},"EmailPreferencesRequest":{"properties":{"marketing_opted_in":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Marketing Opted In"},"product_notifications":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Product Notifications"}},"additionalProperties":false,"type":"object","title":"EmailPreferencesRequest","description":"The fields being changed; an absent field is left exactly as it is.\n\nField-scoped on purpose — and deliberately the opposite of the console's\nsingle-flag toggles. With two independent flags, \"send the full desired\nstate\" makes every tab a writer of *both*: a tab that loaded before the\nnewsletter was turned off elsewhere would faithfully resubmit its stale\ncopy of that flag and silently undo the change. A field that never\ntravels cannot resurrect anything."},"EmailPreferencesView":{"properties":{"marketing_opted_in":{"type":"boolean","title":"Marketing Opted In"},"product_notifications":{"type":"boolean","title":"Product Notifications"},"email":{"type":"string","title":"Email"}},"additionalProperties":false,"type":"object","required":["marketing_opted_in","product_notifications","email"],"title":"EmailPreferencesView","description":"This account's mail settings, and the address they apply to.\n\nThree fields, and the omissions matter as much as the fields: the\nunsubscribe token is a bearer credential and the ESP columns are an\ninternal sync state, so neither is ever serialised here. A console that\nprinted the token would turn every screenshot into a working unsubscribe\nlink for somebody else's account."},"ErrorCode":{"type":"string","enum":["bad_request","unauthorized","forbidden","not_found","method_not_allowed","conflict","validation_failed","rate_limited","internal_error","service_unavailable","not_implemented","plan_limit_exceeded","link_code_invalid","link_code_expired","link_code_consumed","link_code_attempts_exceeded","link_code_limit_exceeded","link_code_denied","link_key_invalid","preauth_key_limit_exceeded","node_not_found","node_already_linked","node_offline","machine_waking","node_revoked","entitlement_required","trial_quota_used","trial_boxes_busy","csrf_failed","auth_transaction_invalid","auth_transaction_expired","auth_identity_invalid","auth_identity_deleting","session_invalid","session_expired","navigation_ticket_invalid","navigation_ticket_expired","navigation_ticket_consumed","proxy_session_invalid","proxy_session_expired","origin_forbidden","hosted_private","hosted_disabled","visitor_session_required","custom_domains.pro_required","custom_domains.limit_reached","custom_domains.apex_unsupported","custom_domains.taken","custom_domains.not_found","access_revoked","webhook_signature_invalid","already_subscribed","billing_account_missing","oauth_invalid_request","oauth_invalid_client","oauth_invalid_grant","oauth_unsupported_grant_type","oauth_invalid_scope","oauth_access_denied","oauth_server_error","oauth_temporarily_unavailable","oauth_invalid_token","oauth_insufficient_scope","protocol_version_unsupported","node_authentication_failed","relay_frame_invalid","relay_destination_invalid","relay_stream_unknown","relay_stream_limit_exceeded","relay_body_too_large","relay_response_too_large","relay_bandwidth_exceeded","relay_backpressure","relay_draining","relay_stream_timeout"],"title":"ErrorCode","description":"Codes clients may branch on independently of human-readable messages."},"ErrorDetail":{"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","title":"Message"},"status":{"type":"integer","title":"Status"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details"}},"additionalProperties":false,"type":"object","required":["code","message","status"],"title":"ErrorDetail","description":"The stable body nested under the envelope's ``error`` key."},"ErrorEnvelope":{"properties":{"error":{"$ref":"#/components/schemas/ErrorDetail"}},"additionalProperties":false,"type":"object","required":["error"],"title":"ErrorEnvelope","description":"Response model used by all cloud API errors."},"FamilyView":{"properties":{"family_id":{"type":"string","format":"uuid","title":"Family Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"last_used_at":{"type":"string","format":"date-time","title":"Last Used At"}},"additionalProperties":false,"type":"object","required":["family_id","created_at","expires_at","last_used_at"],"title":"FamilyView","description":"One live connection under a grant: a refresh family and its deadline."},"GitHubAppSourceView":{"properties":{"operation":{"anyOf":[{"$ref":"#/components/schemas/ServiceOperationView"},{"type":"null"}]},"runtime_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Runtime Status"},"service_name":{"type":"string","title":"Service Name"},"repo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Repo"},"ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ref"},"sha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sha"},"phase":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phase"},"last_nudge":{"anyOf":[{"$ref":"#/components/schemas/GitHubSourceNudgeView"},{"type":"null"}]},"last_status":{"anyOf":[{"$ref":"#/components/schemas/GitHubSourceStatusView"},{"type":"null"}]},"polling_sha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Polling Sha"}},"additionalProperties":false,"type":"object","required":["service_name","repo","ref","sha","phase","last_nudge","last_status","polling_sha"],"title":"GitHubAppSourceView","description":"One git-sourced app on a node: where it comes from and how the last push fared.\n\n``repo``/``ref``/``sha``/``phase`` are the node's own answer (the canonical\n``host/owner/name``, the deployed ref, the sha of the current generation\nand its phase), ``None`` when the node is offline or the app is only known\nfrom a nudge record. ``last_nudge`` lives in this process's memory like the\ntoken memos — a web restart forgets it; ``last_status`` is durable\n(``github_status_posts``). ``polling_sha`` is set while a status poll for\nthat sha is in flight."},"GitHubBuildPlan":{"properties":{"preset":{"anyOf":[{"type":"string","enum":["node","nextjs","python","dockerfile"]},{"type":"null"}],"title":"Preset"},"framework":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Framework"},"node_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Node Version"},"package_manager":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Package Manager"},"install":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Install"},"build":{"anyOf":[{"type":"string"},{"type":"boolean","const":false},{"type":"null"}],"title":"Build"},"start":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start"},"subdir":{"type":"string","title":"Subdir","default":"."},"public_env":{"additionalProperties":{"type":"string"},"type":"object","title":"Public Env"},"commit_sha":{"anyOf":[{"type":"string","pattern":"^[0-9a-f]{40,64}$"},{"type":"null"}],"title":"Commit Sha"},"sources":{"additionalProperties":{"type":"string","enum":["request","saved","config","detected","dockerfile"]},"type":"object","title":"Sources"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"}},"type":"object","title":"GitHubBuildPlan","description":"Project only the daemon's public build fields, never its full response."},"GitHubBuildPreviewView":{"properties":{"build":{"$ref":"#/components/schemas/GitHubBuildPlan"},"port":{"type":"integer","maximum":65535.0,"minimum":1.0,"title":"Port"},"node_versions":{"items":{"type":"string"},"type":"array","title":"Node Versions"},"presets":{"items":{"type":"string","enum":["node","nextjs","python","dockerfile"]},"type":"array","title":"Presets"},"public_env":{"type":"boolean","title":"Public Env","default":false}},"type":"object","required":["build","port"],"title":"GitHubBuildPreviewView","description":"Advisory build inspection; no service or auto-deploy setting is written."},"GitHubBuildSettings":{"properties":{"preset":{"anyOf":[{"type":"string","enum":["node","nextjs","python","dockerfile"]},{"type":"null"}],"title":"Preset"},"install":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"title":"Install"},"build":{"anyOf":[{"type":"string"},{"type":"boolean","const":false},{"type":"null"}],"title":"Build"},"start":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"title":"Start"},"node_version":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Node Version"},"package_manager":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Package Manager"},"subdir":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Subdir"},"public_env":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Public Env"}},"additionalProperties":false,"type":"object","title":"GitHubBuildSettings","description":"Explicit overrides only; null resets a field and false disables the build."},"GitHubDeployNextStep":{"properties":{"tool":{"type":"string","title":"Tool"},"args":{"additionalProperties":true,"type":"object","title":"Args"},"why":{"type":"string","title":"Why"}},"additionalProperties":false,"type":"object","required":["tool","args","why"],"title":"GitHubDeployNextStep","description":"The daemon's machine-shaped \"what now\" (``{tool, args, why}``)."},"GitHubDeployRequest":{"properties":{"repo":{"type":"string","maxLength":200,"minLength":3,"title":"Repo"},"name":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"},{"type":"null"}],"title":"Name"},"ref":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Ref"},"auto_deploy":{"type":"boolean","title":"Auto Deploy","default":true},"port":{"anyOf":[{"type":"integer","maximum":65535.0,"minimum":1.0},{"type":"null"}],"title":"Port"},"build_settings":{"anyOf":[{"$ref":"#/components/schemas/GitHubBuildSettings"},{"type":"null"}]},"project_id":{"anyOf":[{"type":"string","maxLength":20,"minLength":20,"pattern":"^prj_[a-z2-7]{16}$"},{"type":"null"}],"title":"Project Id"},"service":{"anyOf":[{"type":"string","maxLength":20,"pattern":"^(?!.*--)[a-z0-9]([a-z0-9-]{0,18}[a-z0-9])?$"},{"type":"null"}],"title":"Service"}},"additionalProperties":false,"type":"object","required":["repo"],"title":"GitHubDeployRequest","description":"Deploy ``repo`` on a node from the console (D-GH-C6 \"Deploy from GitHub\").\n\n``repo`` is ``owner/name`` of a repository one of this account's *live*\ninstallations grants; ``name`` is the service name the daemon will use\n(its own DNS-label grammar); ``ref`` is a branch or tag, the repository's\ndefault branch when omitted; ``auto_deploy`` defaults **on** (daemon plan\n§6 Q1): a push to ``ref`` redeploys the app.\n\n\"Add service\": ``project_id`` + ``service`` (both or neither) deploy into\nan existing owned project instead of naming a new one; the machine labels\nthe service ``<service>--<project>`` and ``name`` is not sent. A joined\nrow refuses ``auto_deploy``, so asking for it is a 422 here."},"GitHubDeployView":{"properties":{"operation":{"anyOf":[{"$ref":"#/components/schemas/ServiceOperationView"},{"type":"null"}]},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"service_name":{"type":"string","title":"Service Name"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"},"installation_id":{"type":"integer","title":"Installation Id"},"repo_full_name":{"type":"string","title":"Repo Full Name"},"ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ref"},"auto_deploy":{"type":"boolean","title":"Auto Deploy"},"auto_deploy_applied":{"type":"boolean","title":"Auto Deploy Applied"},"picked":{"type":"boolean","title":"Picked"},"token":{"type":"string","enum":["applied","unchanged"],"title":"Token"},"summary":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Summary"},"hints":{"items":{"type":"string"},"type":"array","title":"Hints"},"next_step":{"anyOf":[{"$ref":"#/components/schemas/GitHubDeployNextStep"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["node_id","service_name","installation_id","repo_full_name","ref","auto_deploy","auto_deploy_applied","picked","token","summary","hints","next_step"],"title":"GitHubDeployView","description":"What \"Deploy from GitHub\" did, step by step, and what the daemon said next.\n\n``picked`` is whether the repository was *added* to the node's picker by\nthis request (``False`` when it was already picked); ``token`` is the\npush outcome for the repository's installation (``applied`` or\n``unchanged`` — anything else is an error, not a view);\n``auto_deploy_applied`` is whether the ``[deploy] auto_deploy`` write\nlanded — the deploy itself is already registered when it fails, so the\nview says so rather than failing. ``summary``/``hints``/``next_step`` are\nthe daemon's own, passed through the way its CLI and MCP clients show\nthem. ``project_id`` is the daemon's own project id (``prj_…``), read off\nits 201; None from a daemon that predates the project noun."},"GitHubInstallationReposView":{"properties":{"installation_id":{"type":"integer","title":"Installation Id"},"removed":{"type":"boolean","title":"Removed"},"repos":{"items":{"$ref":"#/components/schemas/GitHubRepoView"},"type":"array","title":"Repos"}},"additionalProperties":false,"type":"object","required":["installation_id","removed","repos"],"title":"GitHubInstallationReposView"},"GitHubInstallationView":{"properties":{"installation_id":{"type":"integer","title":"Installation Id"},"account_login":{"type":"string","title":"Account Login"},"account_type":{"type":"string","title":"Account Type"},"repos_selection":{"type":"string","title":"Repos Selection"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"removed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Removed At"},"suspended_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Suspended At"},"repos_count":{"type":"integer","title":"Repos Count"},"configure_href":{"type":"string","title":"Configure Href"}},"additionalProperties":false,"type":"object","required":["installation_id","account_login","account_type","repos_selection","created_at","removed_at","suspended_at","repos_count","configure_href"],"title":"GitHubInstallationView","description":"One App installation bound to this account, as the console lists it."},"GitHubNodeRepoView":{"properties":{"repo_id":{"type":"integer","title":"Repo Id"},"full_name":{"type":"string","title":"Full Name"},"private":{"type":"boolean","title":"Private"},"installation_id":{"type":"integer","title":"Installation Id"},"account_login":{"type":"string","title":"Account Login"},"picked":{"type":"boolean","title":"Picked"}},"additionalProperties":false,"type":"object","required":["repo_id","full_name","private","installation_id","account_login","picked"],"title":"GitHubNodeRepoView","description":"One repository the node's owner could pick for it, and whether it is picked."},"GitHubNodeReposRequest":{"properties":{"repo_ids":{"items":{"type":"integer"},"type":"array","maxItems":200,"title":"Repo Ids"}},"additionalProperties":false,"type":"object","title":"GitHubNodeReposRequest","description":"Replace the picked set. ``repo_ids`` must all be grantable to this account."},"GitHubNodeReposView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"repos":{"items":{"$ref":"#/components/schemas/GitHubNodeRepoView"},"type":"array","title":"Repos"}},"additionalProperties":false,"type":"object","required":["node_id","repos"],"title":"GitHubNodeReposView","description":"The picker for one node (D-GH-C4): every grantable repo, picked or not.\n\nOnly repositories of *live* installations are listed — an uninstalled or\nsuspended App cannot mint — and a pick that survived its installation's\nremoval is simply not shown, the way its token stopped."},"GitHubNodeSourcesView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"online":{"type":"boolean","title":"Online"},"apps":{"items":{"$ref":"#/components/schemas/GitHubAppSourceView"},"type":"array","title":"Apps"},"truncated":{"type":"boolean","title":"Truncated"}},"additionalProperties":false,"type":"object","required":["node_id","online","apps","truncated"],"title":"GitHubNodeSourcesView","description":"The source cards of one node (D-GH-C6)."},"GitHubRepoView":{"properties":{"repo_id":{"type":"integer","title":"Repo Id"},"full_name":{"type":"string","title":"Full Name"},"private":{"type":"boolean","title":"Private"}},"additionalProperties":false,"type":"object","required":["repo_id","full_name","private"],"title":"GitHubRepoView","description":"One repository an installation grants."},"GitHubSourceNudgeView":{"properties":{"delivery_id":{"type":"string","title":"Delivery Id"},"repo_full_name":{"type":"string","title":"Repo Full Name"},"ref":{"type":"string","title":"Ref"},"sha":{"type":"string","title":"Sha"},"disposition":{"type":"string","enum":["matched","ignored","deduped"],"title":"Disposition"},"at":{"type":"string","format":"date-time","title":"At"}},"additionalProperties":false,"type":"object","required":["delivery_id","repo_full_name","ref","sha","disposition","at"],"title":"GitHubSourceNudgeView","description":"The last nudge one app saw (D-GH-C6: \"incl. ``nudge_ignored``\")."},"GitHubSourceStatusView":{"properties":{"state":{"type":"string","title":"State"},"sha":{"type":"string","title":"Sha"},"posted_at":{"type":"string","format":"date-time","title":"Posted At"}},"additionalProperties":false,"type":"object","required":["state","sha","posted_at"],"title":"GitHubSourceStatusView","description":"The last commit status this cloud posted for one app."},"GitHubView":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"install_href":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Install Href"},"installations":{"items":{"$ref":"#/components/schemas/GitHubInstallationView"},"type":"array","title":"Installations"}},"additionalProperties":false,"type":"object","required":["enabled","install_href","installations"],"title":"GitHubView","description":"What ``/integrations/github`` renders from."},"GitHubWebhookAck":{"properties":{"status":{"type":"string","enum":["accepted","duplicate"],"title":"Status"}},"additionalProperties":false,"type":"object","required":["status"],"title":"GitHubWebhookAck","description":"The ``202`` every verified delivery gets, whatever happens next."},"GrantRevocationView":{"properties":{"revoked_families":{"type":"integer","title":"Revoked Families"},"closed_streams":{"type":"integer","title":"Closed Streams"}},"additionalProperties":false,"type":"object","required":["revoked_families","closed_streams"],"title":"GrantRevocationView","description":"What withdrawing one grant — or one family — actually ended.\n\nZeros are a success: the subject was already revoked, which is the state\nthe caller asked for. ``closed_streams`` counts the live streams this\nrequest terminated, which is what makes the thirty-second bound (W-I8)\nsomething the page can show rather than only promise."},"GrantView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"client_id":{"type":"string","title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"client_revoked":{"type":"boolean","title":"Client Revoked"},"self_registered":{"type":"boolean","title":"Self Registered"},"redirect_uris":{"items":{"type":"string"},"type":"array","title":"Redirect Uris"},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_slug":{"type":"string","title":"Node Slug"},"node_name":{"type":"string","title":"Node Name"},"role":{"type":"string","const":"submitter","title":"Role"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"granted_at":{"type":"string","format":"date-time","title":"Granted At"},"families":{"items":{"$ref":"#/components/schemas/FamilyView"},"type":"array","title":"Families"}},"additionalProperties":false,"type":"object","required":["id","client_id","client_name","client_revoked","self_registered","redirect_uris","node_id","node_slug","node_name","role","scopes","granted_at","families"],"title":"GrantView","description":"One approval — an app, a node, and the connections it authorized."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HookDeliveriesView":{"properties":{"items":{"items":{"$ref":"#/components/schemas/HookDeliveryView"},"type":"array","title":"Items"},"next_before":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Before"},"next_before_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Next Before Id"}},"additionalProperties":false,"type":"object","required":["items","next_before","next_before_id"],"title":"HookDeliveriesView","description":"One page, newest first; pass both ``next_*`` back as ``before`` / ``before_id``."},"HookDeliveryView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"received_at":{"type":"string","format":"date-time","title":"Received At"},"state":{"type":"string","enum":["queued","delivered","rejected","failed"],"title":"State"},"attempts":{"type":"integer","title":"Attempts"},"last_status":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Status"},"next_attempt_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Attempt At"},"delivered_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Delivered At"},"body_sha256":{"type":"string","title":"Body Sha256"},"redeliverable":{"type":"boolean","title":"Redeliverable"}},"additionalProperties":false,"type":"object","required":["id","received_at","state","attempts","last_status","next_attempt_at","delivered_at","body_sha256","redeliverable"],"title":"HookDeliveryView","description":"One delivery's metadata. No hook id, address, headers, query or body."},"HostedDisabledRequest":{"properties":{"disabled":{"type":"boolean","title":"Disabled"}},"additionalProperties":false,"type":"object","required":["disabled"],"title":"HostedDisabledRequest","description":"The owner's takedown switch, in both directions."},"HostedDisabledView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"hosted_disabled":{"type":"boolean","title":"Hosted Disabled"}},"additionalProperties":false,"type":"object","required":["node_id","hosted_disabled"],"title":"HostedDisabledView","description":"The switch as it now stands — the state, never the transition."},"HostedShareView":{"properties":{"service_name":{"type":"string","title":"Service Name"},"access":{"type":"string","enum":["private","public"],"title":"Access"},"state":{"type":"string","enum":["ready","link_down","not_entitled","pending"],"title":"State"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"open_url":{"type":"string","title":"Open Url"}},"additionalProperties":false,"type":"object","required":["service_name","access","state","url","open_url"],"title":"HostedShareView","description":"One shared service on one node, as the node itself reports it.\n\nEvery field but ``open_url`` is the daemon's answer passed through, because\nthe daemon is the only holder of the share itself (D-P26-H1; the cloud keeps\nonly who else may open a private one, ADR-W9): ``access`` is the\nstored exposure mode, ``state`` is the daemon's machine token for whether\nthe share can serve *now*, and ``url`` is the address the daemon composed\nfrom the domain this cloud told it about. ``open_url`` is the one thing the\ncloud adds: where the owner's browser goes to be let in, which is a ticket\nthis deployment mints and therefore a path on this deployment."},"JsonValue":{},"LinkClaimRequest":{"properties":{"code":{"type":"string","maxLength":128,"minLength":1,"title":"Code"},"credential_reference":{"type":"string","maxLength":255,"minLength":1,"title":"Credential Reference"}},"additionalProperties":false,"type":"object","required":["code","credential_reference"],"title":"LinkClaimRequest","description":"The daemon presents the plaintext code exactly once."},"LinkClaimResponse":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_slug":{"type":"string","title":"Node Slug"},"nodes_base_domain":{"type":"string","title":"Nodes Base Domain"}},"additionalProperties":false,"type":"object","required":["node_id","node_slug","nodes_base_domain"],"title":"LinkClaimResponse","description":"Non-secret node identity; capability material is never issued here."},"LinkCodeMintRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"node_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Node Id"}},"additionalProperties":false,"type":"object","title":"LinkCodeMintRequest","description":"Ask for a code, optionally naming the node it will link.\n\n``node_id`` selects the reissue leg (D-LC4): the named node keeps its slug\nand its open codes are revoked, so a code the user lost is replaced rather\nthan added to. Omitted — which is what the console sends — a new node row is\nminted with the code."},"LinkCodeMintResponse":{"properties":{"code":{"type":"string","title":"Code"},"link_code_id":{"type":"string","format":"uuid","title":"Link Code Id"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_slug":{"type":"string","title":"Node Slug"}},"additionalProperties":false,"type":"object","required":["code","link_code_id","expires_at","node_id","node_slug"],"title":"LinkCodeMintResponse","description":"The one and only time the plaintext code leaves this deployment.\n\nNothing stores it — the row holds ``sha256_hex`` alone — so this body is\nunrepeatable: a client that loses it revokes the code and mints another."},"LinkCodeView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_slug":{"type":"string","title":"Node Slug"},"status":{"type":"string","title":"Status"},"attempt_count":{"type":"integer","title":"Attempt Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"additionalProperties":false,"type":"object","required":["id","node_id","node_slug","status","attempt_count","created_at","expires_at"],"title":"LinkCodeView","description":"One open code as metadata: never the plaintext, never the hash."},"LinkCodesView":{"properties":{"link_codes":{"items":{"$ref":"#/components/schemas/LinkCodeView"},"type":"array","title":"Link Codes"}},"additionalProperties":false,"type":"object","required":["link_codes"],"title":"LinkCodesView","description":"Every code the signed-in account still keeps open."},"LinkMetadataView":{"properties":{"nodes_base_domain":{"type":"string","title":"Nodes Base Domain"}},"additionalProperties":false,"type":"object","required":["nodes_base_domain"],"title":"LinkMetadataView","description":"What a linked daemon re-reads to learn this deployment's hosted suffix.\n\nDeliberately one field. It is the *refresh* half of the claim response\n(``nerdit link refresh`` → ``POST /link/refresh`` on the daemon), for the\nnode that linked before P26 or whose cloud moved its wildcard, and adding\nanything account-shaped to it would make an unauthenticated route answer a\nquestion about somebody — which is why the hosted entitlement bit is\n**not** here (D-HC-6 defers that to ADR-HC-2)."},"LogoutAllView":{"properties":{"revoked_sessions":{"type":"integer","title":"Revoked Sessions"}},"additionalProperties":false,"type":"object","required":["revoked_sessions"],"title":"LogoutAllView"},"ManagedAllocationView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"state":{"type":"string","title":"State"},"failure_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Code"},"node_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Node Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"ready_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Ready At"},"kind":{"type":"string","enum":["paid","trial"],"title":"Kind"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"sleeps_after_idle_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sleeps After Idle Minutes"}},"additionalProperties":false,"type":"object","required":["id","state","failure_code","node_id","created_at","ready_at","kind","expires_at","sleeps_after_idle_minutes"],"title":"ManagedAllocationView","description":"One hosted machine's progress, without any provider detail."},"ManagedBackupView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"state":{"type":"string","title":"State"},"result_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Result Code"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Finished At"},"size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size Bytes"}},"additionalProperties":false,"type":"object","required":["id","state","result_code","started_at","finished_at","size_bytes"],"title":"ManagedBackupView","description":"One backup run: never the object key, a checksum or any provider detail."},"ManagedBackupsView":{"properties":{"last_success_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Success At"},"restore_points":{"type":"integer","title":"Restore Points"},"stale":{"type":"boolean","title":"Stale"},"items":{"items":{"$ref":"#/components/schemas/ManagedBackupView"},"type":"array","title":"Items"}},"additionalProperties":false,"type":"object","required":["last_success_at","restore_points","stale","items"],"title":"ManagedBackupsView","description":"The hosted machine's backup health and its newest runs."},"ManagedPurchaseView":{"properties":{"state":{"type":"string","enum":["none","asked","invoiced","paid","lapsed","full"],"title":"State"},"requested_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Requested At"},"paid_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Paid Until"},"trials_left":{"type":"integer","title":"Trials Left"},"trial_available":{"type":"boolean","title":"Trial Available"}},"additionalProperties":false,"type":"object","required":["state","requested_at","paid_until","trials_left","trial_available"],"title":"ManagedPurchaseView","description":"Where the account's hosted-machine purchase stands (PR 9).\n\nNever the invoice reference or the cohort seat: both are operator data."},"ManagedUpdateRequest":{"properties":{"version":{"type":"string","pattern":"^[0-9]{1,6}\\.[0-9]{1,6}\\.[0-9]{1,6}$","title":"Version"}},"additionalProperties":false,"type":"object","required":["version"],"title":"ManagedUpdateRequest"},"ManagedUpdateResultView":{"properties":{"outcome":{"type":"string","enum":["failed","not_started"],"title":"Outcome"},"version":{"type":"string","title":"Version"},"at":{"type":"string","format":"date-time","title":"At"}},"additionalProperties":false,"type":"object","required":["outcome","version","at"],"title":"ManagedUpdateResultView","description":"The newest update attempt that did not succeed."},"ManagedUpdateView":{"properties":{"current":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current"},"available":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Available"},"supported":{"type":"boolean","title":"Supported"},"updating_to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updating To"},"last":{"anyOf":[{"$ref":"#/components/schemas/ManagedUpdateResultView"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["current","available","supported","updating_to","last"],"title":"ManagedUpdateView","description":"Where the box's daemon version stands against the box template's."},"ManagedWakeView":{"properties":{"power":{"type":"string","enum":["on","waking"],"title":"Power"}},"additionalProperties":false,"type":"object","required":["power"],"title":"ManagedWakeView","description":"``waking`` (202): the box is starting, the machines list shows it online\nwhen it is; ``on`` (200): it was not asleep."},"NavigationAuthorizationRequest":{"properties":{"ticket":{"type":"string","maxLength":512,"minLength":1,"title":"Ticket"}},"additionalProperties":false,"type":"object","required":["ticket"],"title":"NavigationAuthorizationRequest","description":"Fragment ticket presented with its HttpOnly browser binding."},"NavigationAuthorizationView":{"properties":{"proof":{"type":"string","title":"Proof"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"additionalProperties":false,"type":"object","required":["proof","expires_at"],"title":"NavigationAuthorizationView","description":"Short-lived browser proof required by the node-host exchange."},"NodeAcmeView":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":false},"listening":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Listening"}},"additionalProperties":false,"type":"object","title":"NodeAcmeView"},"NodeAiAppKeyRevokeView":{"properties":{"service":{"type":"string","title":"Service"},"revoked":{"type":"integer","title":"Revoked"}},"additionalProperties":false,"type":"object","required":["service","revoked"],"title":"NodeAiAppKeyRevokeView"},"NodeAiAppKeyView":{"properties":{"service":{"type":"string","title":"Service"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"additionalProperties":false,"type":"object","required":["service","created_at"],"title":"NodeAiAppKeyView","description":"An app holding an active gateway key, and since when. Never the key or its hash."},"NodeAiAppKeysView":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/NodeAiAppKeyView"},"type":"array","title":"Keys"}},"additionalProperties":false,"type":"object","required":["keys"],"title":"NodeAiAppKeysView"},"NodeAiBindingSpec":{"properties":{"provider":{"type":"string","enum":["api","ollama","gateway"],"title":"Provider"},"model":{"type":"string","maxLength":256,"minLength":1,"title":"Model"},"base_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Base Url"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key"}},"additionalProperties":false,"type":"object","required":["provider","model"],"title":"NodeAiBindingSpec","description":"One ``[ai.<name>]`` spec. ``api_key`` is a reference or nothing."},"NodeAiBindingView":{"properties":{"name":{"type":"string","title":"Name"},"provider":{"type":"string","title":"Provider"},"model":{"type":"string","title":"Model"},"base_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Url"},"key_set":{"type":"boolean","title":"Key Set"}},"additionalProperties":false,"type":"object","required":["name","provider","model","base_url","key_set"],"title":"NodeAiBindingView","description":"One binding as the console shows it. ``key_set``, never the key reference."},"NodeAiGatewayView":{"properties":{"online":{"type":"boolean","title":"Online"},"available":{"type":"boolean","title":"Available"},"enabled":{"type":"boolean","title":"Enabled"},"listening":{"type":"boolean","title":"Listening"},"models":{"items":{"$ref":"#/components/schemas/NodeAiModelView"},"type":"array","title":"Models"},"keys":{"items":{"type":"string"},"type":"array","title":"Keys"}},"additionalProperties":false,"type":"object","required":["online","available","enabled","listening","models","keys"],"title":"NodeAiGatewayView","description":"One machine's AI gateway: its state, its models and the keys they can use."},"NodeAiKeysView":{"properties":{"shared":{"items":{"type":"string"},"type":"array","title":"Shared"},"service":{"items":{"type":"string"},"type":"array","title":"Service"}},"additionalProperties":false,"type":"object","required":["shared","service"],"title":"NodeAiKeysView","description":"Key names the binding form can pick from: the machine's, then the app's."},"NodeAiModelRemoveView":{"properties":{"online":{"type":"boolean","title":"Online"},"alias":{"type":"string","title":"Alias"},"used_by":{"items":{"type":"string"},"type":"array","title":"Used By"}},"additionalProperties":false,"type":"object","required":["online","alias","used_by"],"title":"NodeAiModelRemoveView"},"NodeAiModelView":{"properties":{"alias":{"type":"string","title":"Alias"},"provider":{"type":"string","title":"Provider"},"model":{"type":"string","title":"Model"},"base_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Url"},"key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key"}},"additionalProperties":false,"type":"object","required":["alias","provider","model","base_url","key"],"title":"NodeAiModelView","description":"One alias as the page lists it. ``key`` is the referenced key's **name**."},"NodeAiModelWrite":{"properties":{"model":{"type":"string","maxLength":200,"minLength":1,"pattern":"^[\\x21-\\x7e]+$","title":"Model"},"base_url":{"type":"string","maxLength":2048,"minLength":1,"title":"Base Url"},"key":{"type":"string","maxLength":256,"minLength":1,"pattern":"^[A-Z][A-Z0-9_]*$","title":"Key"}},"additionalProperties":false,"type":"object","required":["model","base_url","key"],"title":"NodeAiModelWrite","description":"A model to add: the provider's model and base URL, and the key's **name**."},"NodeAiModelWriteView":{"properties":{"online":{"type":"boolean","title":"Online"},"alias":{"type":"string","title":"Alias"},"created":{"type":"boolean","title":"Created"},"enabled":{"type":"boolean","title":"Enabled"}},"additionalProperties":false,"type":"object","required":["online","alias","created","enabled"],"title":"NodeAiModelWriteView","description":"What the write came to."},"NodeAiProviderModelsRequest":{"properties":{"key":{"type":"string","maxLength":256,"minLength":1,"pattern":"^[A-Z][A-Z0-9_]*$","title":"Key"},"base_url":{"type":"string","maxLength":2048,"minLength":1,"title":"Base Url"}},"additionalProperties":false,"type":"object","required":["key","base_url"],"title":"NodeAiProviderModelsRequest","description":"Which key, and which provider, to list models for. The key is a **name**."},"NodeAiProviderModelsView":{"properties":{"online":{"type":"boolean","title":"Online"},"models":{"items":{"type":"string"},"type":"array","title":"Models"}},"additionalProperties":false,"type":"object","required":["online","models"],"title":"NodeAiProviderModelsView","description":"The model ids one provider lists for one of the machine's keys."},"NodeAiUsageRowView":{"properties":{"service":{"type":"string","title":"Service"},"alias":{"type":"string","title":"Alias"},"requests":{"type":"integer","title":"Requests"},"prompt_tokens":{"type":"integer","title":"Prompt Tokens"},"completion_tokens":{"type":"integer","title":"Completion Tokens"}},"additionalProperties":false,"type":"object","required":["service","alias","requests","prompt_tokens","completion_tokens"],"title":"NodeAiUsageRowView","description":"One app's calls to one model, summed over the window."},"NodeAiUsageView":{"properties":{"days":{"type":"integer","title":"Days"},"rows":{"items":{"$ref":"#/components/schemas/NodeAiUsageRowView"},"type":"array","title":"Rows"}},"additionalProperties":false,"type":"object","required":["days","rows"],"title":"NodeAiUsageView","description":"The machine AI gateway's usage over the last ``days`` UTC days. No cost."},"NodeAiView":{"properties":{"online":{"type":"boolean","title":"Online"},"service":{"type":"string","title":"Service"},"etag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Etag"},"bindings":{"items":{"$ref":"#/components/schemas/NodeAiBindingView"},"type":"array","title":"Bindings"},"keys":{"$ref":"#/components/schemas/NodeAiKeysView"},"shared_scope":{"type":"boolean","title":"Shared Scope"}},"additionalProperties":false,"type":"object","required":["online","service","etag","bindings","keys","shared_scope"],"title":"NodeAiView","description":"One app's AI bindings and the keys they can reference."},"NodeAiWrite":{"properties":{"bindings":{"additionalProperties":{"anyOf":[{"$ref":"#/components/schemas/NodeAiBindingSpec"},{"type":"null"}]},"type":"object","title":"Bindings"},"etag":{"type":"string","maxLength":128,"minLength":1,"title":"Etag"}},"additionalProperties":false,"type":"object","required":["bindings","etag"],"title":"NodeAiWrite","description":"Bindings to write (``null`` removes one) and the ETag they were read at."},"NodeAiWriteView":{"properties":{"online":{"type":"boolean","title":"Online"},"service":{"type":"string","title":"Service"},"etag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Etag"},"restarted":{"type":"boolean","title":"Restarted"}},"additionalProperties":false,"type":"object","required":["online","service","etag","restarted"],"title":"NodeAiWriteView","description":"What the write came to: the new ETag and whether the app restarted."},"NodeAppTemplatesView":{"properties":{"templates":{"items":{"$ref":"#/components/schemas/AppTemplateView"},"type":"array","title":"Templates"}},"additionalProperties":false,"type":"object","required":["templates"],"title":"NodeAppTemplatesView"},"NodeAuthorizeContextView":{"properties":{"client_id":{"type":"string","title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"self_registered":{"type":"boolean","title":"Self Registered"},"role":{"type":"string","const":"submitter","title":"Role"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"redirect_uri":{"type":"string","title":"Redirect Uri"},"resource_kind":{"type":"string","const":"node","title":"Resource Kind"},"node":{"$ref":"#/components/schemas/ConsentNodeView"},"already_consented":{"type":"boolean","title":"Already Consented"}},"additionalProperties":false,"type":"object","required":["client_id","client_name","self_registered","role","scopes","redirect_uri","resource_kind","node","already_consented"],"title":"NodeAuthorizeContextView","description":"A consent question for one direct node connector."},"NodeBackendsView":{"properties":{"backends":{"items":{"type":"string"},"type":"array","title":"Backends","default":[]},"default_backend":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Backend"}},"additionalProperties":false,"type":"object","title":"NodeBackendsView"},"NodeCapabilitiesView":{"properties":{"version":{"type":"string","title":"Version"},"uptime_s":{"type":"integer","title":"Uptime S","default":0},"gpus":{"$ref":"#/components/schemas/NodeGpuSummaryView"},"models":{"$ref":"#/components/schemas/NodeBackendsView"},"databases":{"$ref":"#/components/schemas/NodeBackendsView"},"proxy":{"$ref":"#/components/schemas/NodeProxySummaryView"},"deploy":{"$ref":"#/components/schemas/NodeDeploySummaryView"},"sandbox":{"anyOf":[{"$ref":"#/components/schemas/NodeSandboxView"},{"type":"null"}]},"mcp_http_enabled":{"type":"boolean","title":"Mcp Http Enabled"},"features":{"items":{"type":"string"},"type":"array","title":"Features"}},"additionalProperties":false,"type":"object","required":["version","gpus","models","databases","proxy","deploy","sandbox","mcp_http_enabled","features"],"title":"NodeCapabilitiesView","description":"What the machine can do. No identity, link, license or network detail."},"NodeClaimRequest":{"properties":{"code":{"type":"string","maxLength":128,"minLength":1,"title":"Code"}},"additionalProperties":false,"type":"object","required":["code"],"title":"NodeClaimRequest","description":"The console submits the plaintext code from the node's own screen."},"NodeClaimResponse":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_slug":{"type":"string","title":"Node Slug"}},"additionalProperties":false,"type":"object","required":["node_id","node_slug"],"title":"NodeClaimResponse","description":"Identity of the node the session now owns."},"NodeClusterStatsView":{"properties":{"gpus_total":{"type":"integer","title":"Gpus Total"},"gpus_in_use":{"type":"integer","title":"Gpus In Use"},"gpus_avg_utilization":{"type":"number","title":"Gpus Avg Utilization"},"services_up":{"type":"integer","title":"Services Up","default":0},"daemon_uptime_seconds":{"type":"integer","title":"Daemon Uptime Seconds"},"daemon_version":{"type":"string","title":"Daemon Version"}},"additionalProperties":false,"type":"object","required":["gpus_total","gpus_in_use","gpus_avg_utilization","daemon_uptime_seconds","daemon_version"],"title":"NodeClusterStatsView"},"NodeDataDirView":{"properties":{"services":{"items":{"$ref":"#/components/schemas/NodeNamedBytesView"},"type":"array","title":"Services"},"services_total_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Services Total Bytes"},"workspaces":{"items":{"$ref":"#/components/schemas/NodeNamedBytesView"},"type":"array","title":"Workspaces"},"workspaces_total_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Workspaces Total Bytes"},"models":{"$ref":"#/components/schemas/NodeModelBytesView"},"archive_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Archive Bytes"},"backups":{"$ref":"#/components/schemas/NodeTarsView"},"volume_backups":{"$ref":"#/components/schemas/NodeTarsView"},"dumps":{"$ref":"#/components/schemas/NodeTarsView"},"dump_staging_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dump Staging Bytes"}},"additionalProperties":false,"type":"object","required":["services","workspaces","models","backups","volume_backups","dumps"],"title":"NodeDataDirView"},"NodeDeploySummaryView":{"properties":{"git_enabled":{"type":"boolean","title":"Git Enabled","default":false},"max_upload_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Upload Bytes"},"max_concurrent_builds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Concurrent Builds"}},"additionalProperties":false,"type":"object","title":"NodeDeploySummaryView"},"NodeDiskView":{"properties":{"docker":{"anyOf":[{"$ref":"#/components/schemas/NodeDockerDiskView"},{"type":"null"}]},"images_total_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Images Total Bytes"},"data_dir":{"$ref":"#/components/schemas/NodeDataDirView"},"orphan_images":{"type":"integer","title":"Orphan Images"},"orphan_data_dirs":{"type":"integer","title":"Orphan Data Dirs"},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"}},"additionalProperties":false,"type":"object","required":["docker","images_total_bytes","data_dir","orphan_images","orphan_data_dirs","warnings"],"title":"NodeDiskView","description":"What Nerdit uses on the machine. Image repo names and orphan names stay there."},"NodeDockerDiskView":{"properties":{"images_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Images Bytes"},"containers_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Containers Bytes"},"volumes_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Volumes Bytes"},"build_cache_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Build Cache Bytes"}},"additionalProperties":false,"type":"object","title":"NodeDockerDiskView"},"NodeDoctorCheckView":{"properties":{"name":{"type":"string","title":"Name"},"status":{"type":"string","title":"Status"},"detail":{"type":"string","title":"Detail"},"latency_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Latency Ms"}},"additionalProperties":false,"type":"object","required":["name","status","detail"],"title":"NodeDoctorCheckView"},"NodeDoctorView":{"properties":{"status":{"type":"string","title":"Status"},"checks":{"items":{"$ref":"#/components/schemas/NodeDoctorCheckView"},"type":"array","title":"Checks"}},"additionalProperties":false,"type":"object","required":["status","checks"],"title":"NodeDoctorView"},"NodeDomainsView":{"properties":{"online":{"type":"boolean","title":"Online"},"service_name":{"type":"string","title":"Service Name"},"domains":{"items":{"$ref":"#/components/schemas/DomainRow"},"type":"array","title":"Domains"}},"additionalProperties":false,"type":"object","required":["online","service_name","domains"],"title":"NodeDomainsView","description":"What one service answers to, and whether the node was reachable to ask."},"NodeEventView":{"properties":{"id":{"type":"integer","title":"Id"},"ts":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Ts"},"type":{"type":"string","title":"Type"},"kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"},"service_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service Name"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"build_version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Build Version"}},"additionalProperties":false,"type":"object","required":["id","type"],"title":"NodeEventView"},"NodeEventsView":{"properties":{"items":{"items":{"$ref":"#/components/schemas/NodeEventView"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"additionalProperties":false,"type":"object","required":["items","next_cursor"],"title":"NodeEventsView","description":"One page of the event feed, newest first. ``data`` stays on the machine."},"NodeGpuSummaryView":{"properties":{"count":{"type":"integer","title":"Count","default":0},"schedulable":{"type":"integer","title":"Schedulable","default":0},"vendors":{"items":{"type":"string"},"type":"array","title":"Vendors","default":[]}},"additionalProperties":false,"type":"object","title":"NodeGpuSummaryView"},"NodeGpuView":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"memory_mb":{"type":"integer","title":"Memory Mb"},"compute_cap":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compute Cap"},"vendor":{"type":"string","title":"Vendor"},"schedulable":{"type":"boolean","title":"Schedulable"},"status":{"type":"string","title":"Status"},"utilization_percent":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Utilization Percent"},"memory_used_mb":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Memory Used Mb"},"temperature_c":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Temperature C"}},"additionalProperties":false,"type":"object","required":["id","name","memory_mb","vendor","schedulable","status"],"title":"NodeGpuView"},"NodeGpusView":{"properties":{"gpus":{"items":{"$ref":"#/components/schemas/NodeGpuView"},"type":"array","title":"Gpus"}},"additionalProperties":false,"type":"object","required":["gpus"],"title":"NodeGpusView"},"NodeHookRemovedView":{"properties":{"service":{"type":"string","title":"Service"},"name":{"type":"string","title":"Name"},"removed":{"type":"boolean","title":"Removed"}},"additionalProperties":false,"type":"object","required":["service","name","removed"],"title":"NodeHookRemovedView","description":"Idempotent: ``removed`` is false when no webhook had that name."},"NodeHookSetView":{"properties":{"name":{"type":"string","title":"Name"},"sender":{"type":"string","title":"Sender"},"path":{"type":"string","title":"Path"},"header":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Header"},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prefix"},"secret_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secret Key"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"hook_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hook Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created":{"type":"boolean","title":"Created"}},"additionalProperties":false,"type":"object","required":["name","sender","path","header","prefix","secret_key","url","hook_id","created_at","created"],"title":"NodeHookSetView","description":"The webhook the machine now holds, and whether this call created it."},"NodeHookView":{"properties":{"name":{"type":"string","title":"Name"},"sender":{"type":"string","title":"Sender"},"path":{"type":"string","title":"Path"},"header":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Header"},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prefix"},"secret_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secret Key"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"hook_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hook Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"additionalProperties":false,"type":"object","required":["name","sender","path","header","prefix","secret_key","url","hook_id","created_at"],"title":"NodeHookView","description":"One live webhook as the console shows it. Never ``secret_ref``."},"NodeHookWrite":{"properties":{"name":{"type":"string","maxLength":32,"minLength":1,"title":"Name"},"sender":{"type":"string","enum":["stripe","github","slack","hmac","none"],"title":"Sender"},"path":{"type":"string","maxLength":512,"minLength":1,"title":"Path"},"header":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Header"},"prefix":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Prefix"}},"additionalProperties":false,"type":"object","required":["name","sender","path"],"title":"NodeHookWrite","description":"A webhook to add. No secret field: S2 carries it, separately."},"NodeHooksView":{"properties":{"online":{"type":"boolean","title":"Online"},"available":{"type":"boolean","title":"Available"},"service":{"type":"string","title":"Service"},"hooks":{"items":{"$ref":"#/components/schemas/NodeHookView"},"type":"array","title":"Hooks"}},"additionalProperties":false,"type":"object","required":["online","available","service","hooks"],"title":"NodeHooksView","description":"One app's webhooks. Offline and an older daemon are states (D-NC9)."},"NodeMdnsView":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":false},"registered":{"type":"boolean","title":"Registered","default":false}},"additionalProperties":false,"type":"object","title":"NodeMdnsView"},"NodeModelBytesView":{"properties":{"ollama":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Ollama"},"huggingface":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Huggingface"}},"additionalProperties":false,"type":"object","title":"NodeModelBytesView"},"NodeModelView":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"backend":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Backend"},"status":{"type":"string","title":"Status"},"desired_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Desired State"},"model_pulled":{"type":"boolean","title":"Model Pulled","default":false},"gpu_count":{"type":"integer","title":"Gpu Count","default":0},"gpu_ids":{"items":{"type":"string"},"type":"array","title":"Gpu Ids","default":[]},"gpu_utilization":{"additionalProperties":{"anyOf":[{"type":"integer"},{"type":"null"}]},"type":"object","title":"Gpu Utilization","default":{}},"max_model_len":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Model Len"},"gpu_memory_utilization":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Gpu Memory Utilization"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"additionalProperties":false,"type":"object","required":["id","name","status","created_at"],"title":"NodeModelView"},"NodeModelsView":{"properties":{"items":{"items":{"$ref":"#/components/schemas/NodeModelView"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"additionalProperties":false,"type":"object","required":["items","next_cursor"],"title":"NodeModelsView","description":"One page of the machine's models, newest first."},"NodeNamedBytesView":{"properties":{"name":{"type":"string","title":"Name"},"bytes":{"type":"integer","title":"Bytes"}},"additionalProperties":false,"type":"object","required":["name","bytes"],"title":"NodeNamedBytesView"},"NodeProxyApexView":{"properties":{"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"present":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Present"},"is_last":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Last"}},"additionalProperties":false,"type":"object","title":"NodeProxyApexView"},"NodeProxyDomainView":{"properties":{"domain":{"type":"string","title":"Domain"},"service_name":{"type":"string","title":"Service Name"},"acme":{"type":"boolean","title":"Acme"},"live":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Live"},"cert_state":{"type":"string","title":"Cert State"}},"additionalProperties":false,"type":"object","required":["domain","service_name","acme","cert_state"],"title":"NodeProxyDomainView"},"NodeProxyRespawnView":{"properties":{"attempts":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Attempts"},"last_spawn_ago_s":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Last Spawn Ago S"},"next_retry_in_s":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Next Retry In S"}},"additionalProperties":false,"type":"object","title":"NodeProxyRespawnView"},"NodeProxyRoutesView":{"properties":{"count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Count"},"live_table":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Live Table"}},"additionalProperties":false,"type":"object","title":"NodeProxyRoutesView"},"NodeProxyStatusView":{"properties":{"state":{"type":"string","title":"State"},"enabled":{"type":"boolean","title":"Enabled"},"available":{"type":"boolean","title":"Available"},"mode":{"type":"string","title":"Mode"},"scheme":{"type":"string","title":"Scheme"},"routes":{"$ref":"#/components/schemas/NodeProxyRoutesView"},"respawn":{"$ref":"#/components/schemas/NodeProxyRespawnView"},"apex":{"$ref":"#/components/schemas/NodeProxyApexView"},"tls_synced":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Tls Synced"},"acme":{"$ref":"#/components/schemas/NodeAcmeView"},"mdns":{"$ref":"#/components/schemas/NodeMdnsView"},"domains":{"items":{"$ref":"#/components/schemas/NodeProxyDomainView"},"type":"array","title":"Domains"}},"additionalProperties":false,"type":"object","required":["state","enabled","available","mode","scheme","routes","respawn","apex","tls_synced","acme","mdns","domains"],"title":"NodeProxyStatusView","description":"The proxy's health. No hostname, port, address, CA or ACME directory."},"NodeProxySummaryView":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":false},"available":{"type":"boolean","title":"Available","default":false},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode"},"scheme":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheme"}},"additionalProperties":false,"type":"object","title":"NodeProxySummaryView"},"NodeRenameRequest":{"properties":{"display_name":{"type":"string","maxLength":1020,"title":"Display Name"}},"additionalProperties":false,"type":"object","required":["display_name"],"title":"NodeRenameRequest","description":"The owner's label for their own machine — never its address."},"NodeRenameView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"display_name":{"type":"string","title":"Display Name"}},"additionalProperties":false,"type":"object","required":["node_id","display_name"],"title":"NodeRenameView","description":"The label as it now stands, after normalization.\n\nWhat was *stored*, not what was typed, so the console redraws from the\nserver the way the share row does rather than from its own input: NFC,\ncontrol stripping and whitespace collapsing all mean the two can differ."},"NodeSandboxView":{"properties":{"drop_all_caps":{"type":"boolean","title":"Drop All Caps"},"no_new_privileges":{"type":"boolean","title":"No New Privileges"},"read_only_rootfs":{"type":"boolean","title":"Read Only Rootfs"}},"additionalProperties":false,"type":"object","required":["drop_all_caps","no_new_privileges","read_only_rootfs"],"title":"NodeSandboxView"},"NodeSecretKeysView":{"properties":{"online":{"type":"boolean","title":"Online"},"service":{"type":"string","title":"Service"},"keys":{"items":{"type":"string"},"type":"array","title":"Keys"}},"additionalProperties":false,"type":"object","required":["online","service","keys"],"title":"NodeSecretKeysView","description":"Which keys one scope holds, by name, and whether the node was reachable.\n\nThere is no value field and there is nowhere to put one: the daemon cannot\nreturn a value, and this cloud must not invent what it does not hold. The\nabsence is the contract."},"NodeSecretValuesWrite":{"properties":{"values":{"additionalProperties":true,"type":"object","title":"Values"}},"additionalProperties":false,"type":"object","required":["values"],"title":"NodeSecretValuesWrite","description":"S2: key → value, the values in this body and nowhere else (rule 1).\n\n``hide_input_in_errors`` keeps a value out of any rendered\n``ValidationError``, and every check below raises a sentence that names no\nkey and no value: a value pasted into the key field must not come back in a\n422 either. The 422 envelope drops ``input`` (``app.py``) but keeps ``loc``,\nwhich is why ``values`` is typed ``Any`` and checked here: a typed dict\nwould put the failing key into the location."},"NodeServiceActionView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"service_name":{"type":"string","title":"Service Name"},"action":{"type":"string","enum":["restart","stop","redeploy","rollback"],"title":"Action"},"service":{"anyOf":[{"$ref":"#/components/schemas/NodeServiceView"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["node_id","service_name","action","service"],"title":"NodeServiceActionView","description":"What one verb came to, in the only terms this cloud can vouch for."},"NodeServiceView":{"properties":{"operation":{"anyOf":[{"$ref":"#/components/schemas/ServiceOperationView"},{"type":"null"}]},"name":{"type":"string","title":"Name"},"service":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"},"kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"last_deploy_phase":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Deploy Phase"},"last_deploy_action":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Deploy Action"},"source_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Type"},"source_repo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Repo"},"source_ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Ref"},"manageable":{"type":"boolean","title":"Manageable"},"rollback_available":{"type":"boolean","title":"Rollback Available"}},"additionalProperties":false,"type":"object","required":["name","kind","status","image","url","last_deploy_phase","last_deploy_action","source_type","source_repo","source_ref","manageable","rollback_available"],"title":"NodeServiceView","description":"One service as the node describes it, with nothing added and nothing kept.\n\nEvery field is the daemon's own, read defensively off its\n``ServiceResponse``. What is deliberately *absent* is as load-bearing as\nwhat is here: no logs and no resource numbers (plan §1 defers both, with\nreasons), and no owner token — ``manageable`` is a boolean precisely so the\nconsole never learns *whose* a service is (D-NC5)."},"NodeServicesView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"online":{"type":"boolean","title":"Online"},"manageable_reported":{"type":"boolean","title":"Manageable Reported"},"services":{"items":{"$ref":"#/components/schemas/NodeServiceView"},"type":"array","title":"Services"},"truncated":{"type":"boolean","title":"Truncated"}},"additionalProperties":false,"type":"object","required":["node_id","online","manageable_reported","services","truncated"],"title":"NodeServicesView","description":"Every service on one node, plus the two facts about the *reading* of it."},"NodeSharesView":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"hosted_public_entitled":{"type":"boolean","title":"Hosted Public Entitled"},"pro_available":{"type":"boolean","title":"Pro Available"},"hosted_disabled":{"type":"boolean","title":"Hosted Disabled"},"shares":{"items":{"$ref":"#/components/schemas/HostedShareView"},"type":"array","title":"Shares"},"truncated":{"type":"boolean","title":"Truncated"}},"additionalProperties":false,"type":"object","required":["node_id","hosted_public_entitled","pro_available","hosted_disabled","shares","truncated"],"title":"NodeSharesView","description":"What one node shares, plus the two facts the *cloud* owns about it.\n\n``shares`` is empty for an offline node rather than an error: a node whose\ndaemon is down still exists, and the console shows the card with nothing in\nit instead of a failure. ``truncated`` says the daemon's first page did not\nexhaust its services — the read is deliberately bounded, and a node with\nmore than a page of them is a scrolling problem, not a paging protocol."},"NodeTarsView":{"properties":{"bytes":{"type":"integer","title":"Bytes","default":0},"count":{"type":"integer","title":"Count","default":0}},"additionalProperties":false,"type":"object","title":"NodeTarsView"},"NodeTemplateDeployRequest":{"properties":{"name":{"anyOf":[{"type":"string","pattern":"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"},{"type":"null"}],"title":"Name"},"project_id":{"anyOf":[{"type":"string","maxLength":20,"minLength":20,"pattern":"^prj_[a-z2-7]{16}$"},{"type":"null"}],"title":"Project Id"},"service":{"anyOf":[{"type":"string","maxLength":20,"pattern":"^(?!.*--)[a-z0-9]([a-z0-9-]{0,18}[a-z0-9])?$"},{"type":"null"}],"title":"Service"}},"additionalProperties":false,"type":"object","title":"NodeTemplateDeployRequest","description":"The service name to deploy the template as, and deliberately nothing else.\n\nOr, to add it to an existing project, ``project_id`` + ``service`` (both or\nneither) in place of ``name``, as on the GitHub deploy."},"NodeTemplateDeployView":{"properties":{"operation":{"anyOf":[{"$ref":"#/components/schemas/ServiceOperationView"},{"type":"null"}]},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"service_name":{"type":"string","title":"Service Name"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id"},"template_id":{"type":"string","title":"Template Id"},"summary":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Summary"},"hints":{"items":{"type":"string"},"type":"array","title":"Hints"},"next_step":{"anyOf":[{"$ref":"#/components/schemas/GitHubDeployNextStep"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["node_id","service_name","template_id","summary","hints","next_step"],"title":"NodeTemplateDeployView","description":"What the deploy created, with the daemon's own ``summary``/``hints``/``next_step``."},"NodeView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"slug":{"type":"string","title":"Slug"},"display_name":{"type":"string","title":"Display Name"},"online":{"type":"boolean","title":"Online"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"uptime_s":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Uptime S"},"last_seen_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Seen At"},"linked_at":{"type":"string","format":"date-time","title":"Linked At"},"dashboard_url":{"type":"string","title":"Dashboard Url"},"hosted_disabled":{"type":"boolean","title":"Hosted Disabled"},"link_state":{"type":"string","enum":["pending","claimed","consumed","linked","expired"],"title":"Link State"},"dormancy_state":{"type":"string","enum":["active","warning","escalated","dormant"],"title":"Dormancy State"},"power":{"anyOf":[{"type":"string","enum":["asleep","waking","stuck"]},{"type":"null"}],"title":"Power"},"ai_console":{"type":"boolean","title":"Ai Console","default":false},"ai_page_v2":{"type":"boolean","title":"Ai Page V2","default":false},"inbound_hooks":{"type":"boolean","title":"Inbound Hooks","default":false}},"additionalProperties":false,"type":"object","required":["id","slug","display_name","online","version","uptime_s","last_seen_at","linked_at","dashboard_url","hosted_disabled","link_state","dormancy_state","power"],"title":"NodeView","description":"One linked node: durable identity plus the current relay projection."},"NodesView":{"properties":{"nodes":{"items":{"$ref":"#/components/schemas/NodeView"},"type":"array","title":"Nodes"}},"additionalProperties":false,"type":"object","required":["nodes"],"title":"NodesView","description":"Every node the signed-in account still keeps linked."},"ObservedView":{"properties":{"state":{"type":"string","enum":["observed","unavailable"],"title":"State"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"}},"additionalProperties":false,"type":"object","required":["state","address"],"title":"ObservedView","description":"Where this machine dialled out from, as the relay saw it.\n\n``unavailable`` covers both \"no connection to have observed\" and \"the\naddress we saw is not one the internet routes to\" — a machine behind the\nsame NAT as the relay, or, in the dev stack, a fake daemon on the compose\nnetwork. The two are one state because the console's answer is the same: we\ncannot compare it against DNS, so we do not pretend to.\n\nRendered live and stored nowhere (D-NC10). It is personal data."},"PortalSessionView":{"properties":{"portal_url":{"type":"string","title":"Portal Url"}},"additionalProperties":false,"type":"object","required":["portal_url"],"title":"PortalSessionView","description":"A short-lived customer-portal URL for self-service plan management."},"PreauthKeyMintRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Name"},"max_uses":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Max Uses"},"ttl_days":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Ttl Days"}},"additionalProperties":false,"type":"object","title":"PreauthKeyMintRequest","description":"What an owner may choose about a key: a label, a fan-out, a lifetime.\n\nAll three are optional, and the deployment's defaults (OD-10: one use,\nninety days, unnamed) are what an empty body asks for — the common case is a\nsingle machine and should not require an owner to have an opinion. The upper\nbounds are *not* here: they are settings, so a static ``le=`` would be a\nsecond copy of a number an owner can ratify, and the two would drift. The\npolicy layer refuses above the cap with the same ``400`` this model would\nhave given."},"PreauthKeyMintResponse":{"properties":{"key":{"type":"string","title":"Key"},"id":{"type":"string","format":"uuid","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"max_uses":{"type":"integer","title":"Max Uses"},"used_count":{"type":"integer","title":"Used Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"additionalProperties":false,"type":"object","required":["key","id","name","max_uses","used_count","created_at","expires_at"],"title":"PreauthKeyMintResponse","description":"The one and only time a key's plaintext leaves this deployment.\n\nNothing stores it, so this body is unrepeatable: a client that loses it\nrevokes the key and mints another. The metadata beside it is what ``GET``\nwill keep answering, so a console can render the new row without a refetch."},"PreauthKeyView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"max_uses":{"type":"integer","title":"Max Uses"},"used_count":{"type":"integer","title":"Used Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"}},"additionalProperties":false,"type":"object","required":["id","name","max_uses","used_count","created_at","expires_at","last_used_at","revoked_at"],"title":"PreauthKeyView","description":"One key as metadata: never the plaintext, never the hash.\n\n``used_count`` counts *distinct* enrollments and not presentations: an\nidempotent replay of a credential the key already enrolled (D-X16-O9)\ndeliberately leaves it — and ``last_used_at`` — alone, so this row answers\n\"how many machines did this key put on my account\", which is the question an\nowner deciding whether to revoke it is actually asking."},"PreauthKeysView":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/PreauthKeyView"},"type":"array","title":"Keys"}},"additionalProperties":false,"type":"object","required":["keys"],"title":"PreauthKeysView","description":"The account's keys, newest first."},"PreauthLinkRequest":{"properties":{"key":{"type":"string","maxLength":43,"minLength":1,"title":"Key"},"credential_reference":{"type":"string","maxLength":255,"minLength":1,"title":"Credential Reference"},"hostname_hint":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Hostname Hint"}},"additionalProperties":false,"type":"object","required":["key","credential_reference"],"title":"PreauthLinkRequest","description":"What a machine sends to enroll itself (D-X16-O7 payload shape).\n\nTwo load-bearing fields and one display string. The key authorises; the\ncredential is what gets enrolled and is the value D-X16-O9's idempotency is\nkeyed on. ``hostname_hint`` is the stem of the slug and the node's initial\ndisplay name — this route is unauthenticated, so it is attacker-chosen text\n(D-X16-O23), bounded here and normalised before it is written."},"ProbeView":{"properties":{"open":{"type":"boolean","title":"Open"},"timed_out":{"type":"boolean","title":"Timed Out"}},"additionalProperties":false,"type":"object","required":["open","timed_out"],"title":"ProbeView","description":"One TCP connect to one port of the observed address."},"ProjectAddress":{"properties":{"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"kind":{"type":"string","enum":["default","hosted","domain"],"title":"Kind"},"state":{"type":"string","title":"State"},"access":{"anyOf":[{"type":"string","enum":["private","public"]},{"type":"null"}],"title":"Access"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"cert_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cert State"}},"type":"object","required":["url","kind","state"],"title":"ProjectAddress"},"ProjectCreate":{"properties":{"name":{"type":"string","pattern":"^(?!.*--)[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$","title":"Name"},"node_id":{"type":"string","format":"uuid","title":"Node Id"}},"additionalProperties":false,"type":"object","required":["name","node_id"],"title":"ProjectCreate"},"ProjectDirectory":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ProjectView"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"sources":{"items":{"$ref":"#/components/schemas/ProjectSource"},"type":"array","title":"Sources"}},"type":"object","required":["items","next_cursor","sources"],"title":"ProjectDirectory"},"ProjectHome":{"properties":{"hostname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hostname"},"node_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Node Id"}},"type":"object","title":"ProjectHome"},"ProjectLiveView":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"namespace":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Namespace"},"services":{"items":{"$ref":"#/components/schemas/NodeServiceView"},"type":"array","title":"Services"},"resources":{"items":{"$ref":"#/components/schemas/ProjectResource"},"type":"array","title":"Resources"},"variables":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProjectVariable"},"type":"array"},{"type":"null"}],"title":"Variables"},"addresses":{"items":{"$ref":"#/components/schemas/ProjectAddress"},"type":"array","title":"Addresses"},"home":{"$ref":"#/components/schemas/ProjectHome"},"rename_supported":{"type":"boolean","title":"Rename Supported"},"deploy_into_project_supported":{"type":"boolean","title":"Deploy Into Project Supported","default":false}},"type":"object","required":["id","name","services","resources","variables","addresses","home","rename_supported"],"title":"ProjectLiveView"},"ProjectRename":{"properties":{"name":{"type":"string","pattern":"^(?!.*--)[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$","title":"Name"}},"additionalProperties":false,"type":"object","required":["name"],"title":"ProjectRename"},"ProjectResource":{"properties":{"service":{"type":"string","title":"Service"},"type":{"type":"string","title":"Type"},"binding":{"type":"string","title":"Binding"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"},"target":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target"},"ready":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ready"}},"type":"object","required":["service","type","binding"],"title":"ProjectResource"},"ProjectSource":{"properties":{"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_name":{"type":"string","title":"Node Name"},"sync":{"$ref":"#/components/schemas/ProjectSyncState"}},"type":"object","required":["node_id","node_name","sync"],"title":"ProjectSource"},"ProjectSyncState":{"properties":{"status":{"type":"string","enum":["pending","synced","offline","unsupported","failed"],"title":"Status","default":"pending"},"synced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Synced At"},"retirement_supported":{"type":"boolean","title":"Retirement Supported","default":false},"bindings_delivered":{"type":"boolean","title":"Bindings Delivered","default":false}},"type":"object","title":"ProjectSyncState","description":"Connection-local discovery status; never a persisted runtime snapshot."},"ProjectVariable":{"properties":{"key":{"type":"string","title":"Key"},"scope":{"type":"string","title":"Scope"},"plain":{"type":"boolean","title":"Plain"}},"type":"object","required":["key","scope","plain"],"title":"ProjectVariable"},"ProjectVariablesResult":{"properties":{"project_id":{"type":"string","title":"Project Id"},"scope":{"type":"string","title":"Scope"},"keys":{"items":{"type":"string"},"type":"array","title":"Keys"},"plain":{"type":"boolean","title":"Plain"}},"type":"object","required":["project_id","scope","keys","plain"],"title":"ProjectVariablesResult"},"ProjectVariablesSet":{"properties":{"values":{"additionalProperties":{"type":"string"},"type":"object","maxProperties":200,"minProperties":1,"title":"Values"},"secret":{"type":"boolean","title":"Secret","default":true}},"additionalProperties":false,"type":"object","required":["values"],"title":"ProjectVariablesSet"},"ProjectView":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"node_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Node Id"},"node_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Node Name"},"sync":{"$ref":"#/components/schemas/ProjectSyncState"}},"type":"object","required":["id","name","node_id","node_name","sync"],"title":"ProjectView"},"ProviderConfigView":{"properties":{"supabase_url":{"type":"string","title":"Supabase Url"},"supabase_publishable_key":{"type":"string","title":"Supabase Publishable Key"},"supabase_oauth_client_id":{"type":"string","title":"Supabase Oauth Client Id"}},"additionalProperties":false,"type":"object","required":["supabase_url","supabase_publishable_key","supabase_oauth_client_id"],"title":"ProviderConfigView"},"ProxySessionRevocationView":{"properties":{"revoked":{"type":"boolean","title":"Revoked"},"closed_streams":{"type":"integer","title":"Closed Streams"}},"additionalProperties":false,"type":"object","required":["revoked","closed_streams"],"title":"ProxySessionRevocationView","description":"What closing one node-dashboard session ended: the session, and nothing else."},"ProxySessionView":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"node_id":{"type":"string","format":"uuid","title":"Node Id"},"node_slug":{"type":"string","title":"Node Slug"},"destination_host":{"type":"string","title":"Destination Host"},"role":{"type":"string","title":"Role"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_seen_at":{"type":"string","format":"date-time","title":"Last Seen At"},"absolute_expires_at":{"type":"string","format":"date-time","title":"Absolute Expires At"}},"additionalProperties":false,"type":"object","required":["id","node_id","node_slug","destination_host","role","created_at","last_seen_at","absolute_expires_at"],"title":"ProxySessionView","description":"One browser's open access to one node dashboard."},"ReachabilityView":{"properties":{"state":{"type":"string","enum":["reachable","partial","unreachable","not_probed"],"title":"State"},"http":{"anyOf":[{"$ref":"#/components/schemas/ProbeView"},{"type":"null"}]},"https":{"anyOf":[{"$ref":"#/components/schemas/ProbeView"},{"type":"null"}]},"https_port":{"type":"integer","title":"Https Port"}},"additionalProperties":false,"type":"object","required":["state","http","https","https_port"],"title":"ReachabilityView","description":"Whether the internet can open a connection to this machine at all.\n\n``not_probed`` is not a failure: it is what an unavailable observed address\nforces, and it is the state the dev stack always shows."},"ReportedMachine":{"properties":{"hostname_hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hostname Hint"},"daemon_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Daemon Version"},"os":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Os"},"verified":{"type":"boolean","const":false,"title":"Verified","default":false}},"additionalProperties":false,"type":"object","title":"ReportedMachine","description":"What the requesting machine said about itself. None of it is verified.\n\nEvery field here is attacker-controlled (D-X16-O23): the mint route that\ncaptured them is unauthenticated, so a phisher writes whatever reads best.\n``verified`` is typed ``False`` rather than defaulted to it, so no later\nedit can produce a payload that claims otherwise, and so the console's label\n— *\"reported by the requesting machine — not verified\"* — is carried by the\ncontract instead of by a comment."},"ResolvedView":{"properties":{"state":{"type":"string","enum":["resolved","unresolved","timed_out"],"title":"State"},"addresses":{"items":{"type":"string"},"type":"array","title":"Addresses"},"private_count":{"type":"integer","title":"Private Count"}},"additionalProperties":false,"type":"object","required":["state","addresses","private_count"],"title":"ResolvedView","description":"Where the name points, as this cloud sees it from where a visitor sits.\n\nSplit in two on purpose. ``addresses`` carries only globally routable\nanswers; a private answer is *counted* and never rendered, because the web\nrole's resolver inside compose is Docker's embedded DNS and printing what it\nsaid would describe this deployment's own network in a console panel.\n``resolved`` therefore means \"the lookup answered something\", whether or not\nany of it is renderable.\n\n``addresses`` is also capped, so a name behind a long round-robin shows a\nsample of its answer rather than the whole of it. The verdict below is\ncomputed from the whole answer and never from this sample."},"SecurityView":{"properties":{"clients":{"items":{"$ref":"#/components/schemas/ClientView"},"type":"array","title":"Clients"},"grants":{"items":{"$ref":"#/components/schemas/GrantView"},"type":"array","title":"Grants"},"proxy_sessions":{"items":{"$ref":"#/components/schemas/ProxySessionView"},"type":"array","title":"Proxy Sessions"},"account_grants":{"items":{"$ref":"#/components/schemas/AccountGrantView"},"type":"array","title":"Account Grants"},"available_account_nodes":{"items":{"$ref":"#/components/schemas/AccountGrantNodeView"},"type":"array","title":"Available Account Nodes"},"max_account_grant_nodes":{"type":"integer","title":"Max Account Grant Nodes","default":25},"available_account_projects":{"items":{"$ref":"#/components/schemas/AccountGrantProjectView"},"type":"array","title":"Available Account Projects"},"max_account_grant_projects":{"type":"integer","title":"Max Account Grant Projects","default":25}},"additionalProperties":false,"type":"object","required":["clients","grants","proxy_sessions"],"title":"SecurityView","description":"Everything reaching this account's machines — or able to again — and what ends it."},"ServiceLogsView":{"properties":{"text":{"type":"string","title":"Text"},"truncated":{"type":"boolean","title":"Truncated"}},"type":"object","required":["text","truncated"],"title":"ServiceLogsView","description":"An ephemeral, bounded owner-requested log snapshot."},"ServiceOperationView":{"properties":{"identity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Identity"},"phase":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phase"},"action":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"failure":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure"}},"additionalProperties":false,"type":"object","title":"ServiceOperationView","description":"Dated operation metadata; arbitrary errors and output never enter this view."},"SessionUserView":{"properties":{"id":{"type":"string","title":"Id"},"email":{"type":"string","title":"Email"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"provider_subject":{"type":"string","title":"Provider Subject"}},"additionalProperties":false,"type":"object","required":["id","email","display_name","provider_subject"],"title":"SessionUserView"},"SessionView":{"properties":{"session_id":{"type":"string","title":"Session Id"},"absolute_expires_at":{"type":"string","title":"Absolute Expires At"},"csrf_token":{"type":"string","title":"Csrf Token"},"user":{"$ref":"#/components/schemas/SessionUserView"},"infrastructure":{"type":"boolean","title":"Infrastructure","default":false},"custom_domains":{"type":"boolean","title":"Custom Domains","default":false}},"additionalProperties":false,"type":"object","required":["session_id","absolute_expires_at","csrf_token","user"],"title":"SessionView"},"ShareAccessRequest":{"properties":{"preserve_existing":{"type":"boolean","title":"Preserve Existing","default":false},"access":{"type":"string","enum":["private","public"],"title":"Access"}},"additionalProperties":false,"type":"object","required":["access"],"title":"ShareAccessRequest","description":"The owner's exposure decision for ONE app (X16: public shares are Pro).\n\nOnly ``access``, and deliberately not the daemon's second field. ``nerditd``'s\n``PUT /api/services/{name}/share`` also takes ``consent: bool``, which it\nrequires (with, or instead of, a ``[deploy].edge_auth`` block) before it will\naccept ``public`` — its own guard against an operator publishing something by\naccident from a script. That flag is **not** re-exposed here, because on this\npath it would be a field whose only honest value is the one the caller\nalready sent: reaching this route at all means an authenticated owner sent a\nsame-origin, CSRF-bearing POST whose entire body is the word ``public``, and\nthe console puts a confirm dialog naming the app in front of it (\"Anyone\nwith the link can open <app>\"). There is no accidental caller to protect.\nForwarding ``consent`` unconditionally would be different and wrong — the\nroute sends it only on ``public``, where it means what the person did."},"UnsubscribeView":{"properties":{"marketing_opted_in":{"type":"boolean","title":"Marketing Opted In","default":false}},"additionalProperties":false,"type":"object","title":"UnsubscribeView","description":"What the route says, which is the same thing in every case.\n\n``marketing_opted_in`` is always ``False`` and is here because the caller\nasked for exactly one outcome and this states it: after this request, this\ntoken's account receives no marketing mail. It is a constant rather than a\nread-back on purpose — a field that varied would re-open the oracle the\nidentical status codes close, by telling an unknown token apart from a\nknown one that was already unsubscribed."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VisitBucket":{"properties":{"hour":{"type":"string","format":"date-time","title":"Hour"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["hour","count"],"title":"VisitBucket"},"WebhookAck":{"properties":{"status":{"type":"string","const":"accepted","title":"Status"}},"additionalProperties":false,"type":"object","required":["status"],"title":"WebhookAck","description":"What Polar is told. Deliberately says nothing about what was decided."}}},"tags":[{"name":"auth","description":"Reserved /api/auth product boundary."},{"name":"link","description":"Reserved /api/link product boundary."},{"name":"account","description":"Deletion of the signed-in account."},{"name":"nodes","description":"Node linking, status projection, and unlink."},{"name":"oauth","description":"MCP authorization consent for the signed-in person."},{"name":"security","description":"Credential inventory and revocation."},{"name":"audit","description":"The signed-in account's own cloud-audit trail."},{"name":"github","description":"GitHub App installations bound to the signed-in account."},{"name":"health","description":"Role liveness and readiness probes."}]}