@portpowered/reference-data 0.4.3 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/generated/guides.json +4 -4
- package/generated/openapi.json +5 -5
- package/generated/public/docs/for-ai/guides/authentication-and-dcr.md +7 -1
- package/generated/public/docs/for-ai/guides/connect-agent.md +2 -0
- package/generated/public/docs/for-ai/guides/index.md +2 -0
- package/generated/public/docs/for-ai/guides/oauth-discovery.md +2 -0
- package/generated/public/docs/for-ai/guides/quickstart.md +4 -2
- package/generated/public/docs/for-ai/index.md +16 -9
- package/generated/public/docs/for-ai/operations/sendMessage.md +4 -2
- package/generated/public/docs/for-ai/operations/sendMessageLegacyAlias.md +4 -2
- package/generated/public/docs/references/manifest.json +28 -26
- package/generated/public/docs/references/openapi.yaml +12 -5
- package/generated/public/docs/references/schemas/CreateMessageResponse.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/generated/guides.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
{
|
|
9
9
|
"id": "authentication-and-dcr",
|
|
10
10
|
"title": "Authentication and dynamic client registration",
|
|
11
|
-
"content": "# Authentication and dynamic client registration\n\nAll supported agents use the same OAuth components. REST and MCP select different protected resources; DCR and CIMD select different registration methods. Neither product branding nor registration grants device access.\n\nThe complete walkthrough below is generated from the canonical components linked here. Read it in one document, or reference the individual components from your connector:\n\n1. [Discover endpoints and choose REST or MCP](/docs/for-ai/guides/oauth-discovery.md).\n2. [Register with DCR or supported CIMD](/docs/for-ai/guides/oauth-client-registration.md). Remote callbacks require HTTPS; local callback registrations contain only localhost/loopback callbacks. Repeated canonical fingerprints reuse the original public client without expanding scopes.\n3. [Obtain user consent and validate the callback](/docs/for-ai/guides/oauth-consent-and-callback.md). PKCE S256 is required. Pause for the user to sign in and approve access.\n4. [Exchange, refresh and revoke tokens](/docs/for-ai/guides/oauth-token-lifecycle.md). Keep the same canonical resource through authorization, exchange and refresh.\n5. [Verify the connection](/docs/for-ai/guides/first-setup.md), then use [the REST quickstart](/docs/for-ai/guides/quickstart.md) or your client's MCP tools.\n\nRead [authorization rules](/docs/for-ai/guides/authorization-rules.md) for scopes, principal/resource policies, sharing and denial diagnosis. Never ask for a Google password, paste credentials into chat, or approve the agent's own grant. Claimed client names such as Muse and Grok remain unverified.\n\n## Discover the OAuth server and choose REST or MCP\n\nThis discovery step is shared by every client. The deployed pilot API is https://api.portoperatingsystem.lol. Fetch its /.well-known/oauth-authorization-server document anonymously. Read issuer, authorization_endpoint, token_endpoint, registration_endpoint, revocation_endpoint and scopes_supported. Use the returned endpoints exactly. An alias can advertise a different canonical Cloud Run issuer; do not substitute the alias into token audiences.\n\n| Interface | API destination | OAuth resource |\n| --- | --- | --- |\n| REST | The returned issuer, plus the documented operation path | The exact returned issuer |\n| MCP | The returned issuer plus /mcp, Streamable HTTP | The resource returned by /.well-known/oauth-protected-resource/mcp, normally issuer plus /mcp |\n\nAn MCP connector can start at https://api.portoperatingsystem.lol/mcp and follow its WWW-Authenticate resource_metadata URL. Fetch that public metadata and the authorization server it names. MCP initialization alone does not prove that the account's devices are accessible. Never use a REST token on MCP or an MCP token on REST.\n\nChoose one supported [client registration method](/docs/for-ai/guides/oauth-client-registration.md), then follow the same [consent and callback steps](/docs/for-ai/guides/oauth-consent-and-callback.md). If metadata omits registration_endpoint, new DCR registration is unavailable; do not guess a replacement endpoint or silently switch to another client's credentials.\n\nSee [scopes and resource rules](/docs/for-ai/guides/authorization-rules.md). Authorization-server scopes_supported describes server vocabulary; a particular client's accepted scopes can be narrower.\n\n### Carry discovery values into every request\n\nThis JavaScript fragment initializes a REST connection. Keep `authorizationServer` and `resource` for the consent and token examples; the API alias is only the discovery starting point.\n\n```javascript\nconst response = await fetch('https://api.portoperatingsystem.lol/.well-known/oauth-authorization-server');\nif (!response.ok) throw new Error('OAuth discovery failed');\nconst authorizationServer = await response.json();\nconst resource = authorizationServer.issuer;\nconst apiBase = authorizationServer.issuer;\n```\n\nFor MCP, assign `resource` from the protected-resource metadata's `resource` field instead. Do not replace either value with the hostname you started from. A wrong `resource` can produce an `invalid_request` error callback even when client registration succeeded.\n\nCanonical component: [Discover the OAuth server and choose REST or MCP](/docs/for-ai/guides/oauth-discovery.md).\n\n## Register an OAuth client with DCR or CIMD\n\nStart with [discovery](/docs/for-ai/guides/oauth-discovery.md). DCR and CIMD identify a client; they do not sign in a user or grant device access. Both use the same [PKCE consent and callback flow](/docs/for-ai/guides/oauth-consent-and-callback.md) and [token lifecycle](/docs/for-ai/guides/oauth-token-lifecycle.md).\n\n### DCR: public clients\n\nPOST application/json to the discovered registration_endpoint:\n\n```json\n{\"client_name\":\"My device assistant\",\"redirect_uris\":[\"https://your-client.example/oauth/callback\"],\"token_endpoint_auth_method\":\"none\",\"grant_types\":[\"authorization_code\",\"refresh_token\"],\"response_types\":[\"code\"],\"scope\":\"endpoint:read message:send\"}\n```\n\nUse a callback the client controls. Remote callbacks require HTTPS. Local clients may use exact HTTP callbacks on localhost or literal loopback IPs, such as http://127.0.0.1:8765/callback. All callbacks in a registration must be local or all must be remote; these sets cannot be mixed. No fragments or user information are permitted. Start the local callback listener before authorization. The website's client-management interface enforces this same callback-set policy.\n\nSuccess is 201 with client_id, client_id_issued_at and accepted metadata. Public clients get no client_secret. Supported DCR scopes are endpoint:read, endpoint:write, message:read, message:send, group:read, group:manage, subscription:read and subscription:write. The default is endpoint:read. Request only the task's necessary scopes and check the returned scope. Broader static-client scopes are outside this anonymous profile.\n\nThe registration fingerprint is SHA-256 of compact canonical JSON with sorted object keys and sorted redirect_uris, grant_types and response_types arrays, plus client_name and token_endpoint_auth_method. Defaults are applied before hashing. Array ordering does not create another client. A matching registration returns the original client ID, issue time, scopes and descriptive metadata. It never overwrites the existing client or expands scopes, even if scope or software_id differs in the new request. Fingerprinting is deduplication, not proof of callback ownership or vendor identity.\n\nDefault quotas are 1,000 new registrations per rolling day and 10,000 retained registrations. Reuse does not consume new-registration quota. A quota failure returns 429 with Retry-After. An operator can disable new registrations without retiring existing grants. Read the [registration operation](/docs/for-ai/operations/registerDynamicOAuthClient.md) for exact request, response and error schemas.\n\n### CIMD: allowlisted metadata clients\n\nA supported CIMD client uses its allowlisted HTTPS metadata URL as client_id instead of posting a DCR request. Port OS retrieves and validates that client metadata, including its exact callbacks and public PKCE profile. Arbitrary metadata URLs and redirects from the metadata fetch are not supported. If your metadata URL is not allowlisted, arrange onboarding or use DCR if your client supports it. Do not pretend another client's metadata URL belongs to you.\n\nClient_name, software_id, software_version and client_uri remain unverified claims. No trusted software-statement issuer is configured; software_statement is rejected. A registration called Muse or Grok does not establish a verified vendor integration or gain additional permissions. Static confidential clients retain their separately configured client authentication method.\n\nCanonical component: [Register an OAuth client with DCR or CIMD](/docs/for-ai/guides/oauth-client-registration.md).\n\n## Obtain user consent and validate the OAuth callback\n\nUse the endpoints and resource from [discovery](/docs/for-ai/guides/oauth-discovery.md) and the client ID and exact callback from [registration](/docs/for-ai/guides/oauth-client-registration.md). These steps are identical for DCR and CIMD, REST and MCP; the chosen resource is the transport-specific input.\n\n1. Start the callback handler the client controls. For a loopback callback, bind its listener locally before opening authorization.\n2. Generate a cryptographically random PKCE verifier of 43–128 permitted characters. Compute code_challenge = BASE64URL(SHA256(verifier)), without padding. Generate a separate random state and retain both in this authorization attempt.\n3. Open the discovered authorization_endpoint with the following query parameters. Percent-encode parameter values; do not append raw callback URLs or scopes into an unescaped query.\n\n```text\nclient_id=<registered ID or supported CIMD metadata URL>\nredirect_uri=<exact registered callback>\nresponse_type=code\nscope=<requested subset of the client's accepted scopes>\nresource=<chosen canonical REST or MCP resource>\nstate=<random client state>\ncode_challenge=<S256 challenge>\ncode_challenge_method=S256\n```\n\nFor REST, use the `resource = authorizationServer.issuer` value retained from discovery. Construct the URL rather than writing an alias into a template. This JavaScript fragment assumes `clientId`, `redirectUri` and `acceptedScope` come from your registration response and callback configuration:\n\n```javascript\nconst base64url = bytes => btoa(String.fromCharCode(...bytes))\n .replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');\nconst verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));\nconst state = base64url(crypto.getRandomValues(new Uint8Array(32)));\nconst challenge = base64url(new Uint8Array(await crypto.subtle.digest(\n 'SHA-256', new TextEncoder().encode(verifier))));\nconst authorizationURL = new URL(authorizationServer.authorization_endpoint);\nauthorizationURL.search = new URLSearchParams({\n client_id: clientId, redirect_uri: redirectUri, response_type: 'code',\n scope: acceptedScope, resource, state,\n code_challenge: challenge, code_challenge_method: 'S256',\n}).toString();\n// Retain verifier, state and authorizationServer.issuer privately until callback validation.\n// Open authorizationURL.href in the user's browser; wait for their consent.\n```\n\nThe user signs in on Port OS and reviews the requested permissions. Pause for the user; an agent must not approve its own grant, request passwords or manufacture a production sign-in identity. Local functional tests substitute an existing signed fixture identity at this boundary without changing production sign-in.\n\nAfter approval, Port OS navigates to the exact callback with code, state and iss. Reject a callback whose state or issuer does not match this attempt. Treat an error callback as a denied or failed authorization, not a code. Codes and pending consent expire after ten minutes and are single-use. Do not paste codes or credentials into shared chat or logs. Continue with [code exchange and token lifecycle](/docs/for-ai/guides/oauth-token-lifecycle.md).\n\nCanonical component: [Obtain user consent and validate the OAuth callback](/docs/for-ai/guides/oauth-consent-and-callback.md).\n\n## Exchange, refresh and revoke scoped OAuth tokens\n\nComplete [callback validation](/docs/for-ai/guides/oauth-consent-and-callback.md) first. For a public DCR/CIMD client, POST application/x-www-form-urlencoded to the discovered token_endpoint with:\n\n```text\ngrant_type=authorization_code\nclient_id=<client ID used for authorization>\ncode=<validated callback code>\nredirect_uri=<the same exact callback>\ncode_verifier=<this attempt's original verifier>\nresource=<the same canonical resource used for authorization>\n```\n\nContinue with the same discovery values and verifier from the consent example. `code` below must be from a callback whose `state` and `iss` you validated; abort on any callback `error`:\n\n```javascript\nconst tokenResponse = await fetch(authorizationServer.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code', client_id: clientId, code,\n redirect_uri: redirectUri, code_verifier: verifier, resource,\n }),\n});\nif (!tokenResponse.ok) throw new Error('OAuth code exchange failed');\nconst tokens = await tokenResponse.json(); // Store securely; do not print tokens.\n```\n\nSave access_token, refresh_token and returned scope in the connector's secure credential store. Public clients have no client secret. Use Authorization: Bearer <access_token> with REST requests or the MCP HTTP connection, according to that token's resource. A refresh token is never an API credential. [Scopes bound access; resource policies still apply](/docs/for-ai/guides/authorization-rules.md).\n\nRefresh with grant_type=refresh_token, client_id, refresh_token and the original resource at token_endpoint. Scope may stay the same or narrow; it cannot expand. Credentials rotate: store the newly returned refresh token atomically and stop using the previous one. A revoked or reused refresh token must not be retried in a loop. Reauthorize when the grant no longer permits the intended operation.\n\nRevoke by POSTing application/x-www-form-urlencoded token=<refresh_token>&client_id=<client_id> to revocation_endpoint. Revoked grants stop authorizing both access and refresh requests. Removing a local configuration file alone is not server-side revocation.\n\nRead [the token operation](/docs/for-ai/operations/generateToken.md), [revocation operation](/docs/for-ai/operations/revokeOAuthGrant.md), and [connection recovery](/docs/for-ai/guides/troubleshooting.md) for schemas and failures. Verify account access using [first setup](/docs/for-ai/guides/first-setup.md), then perform only the customer's intended task. Dispatch acceptance is distinct from observed device state.\n\nCanonical component: [Exchange, refresh and revoke scoped OAuth tokens](/docs/for-ai/guides/oauth-token-lifecycle.md).\n\n## Scopes and authorization rules\n\nAuthentication establishes the principal and client. OAuth scopes bound the user's delegated authority; resource rules decide which selected resources and actions that principal may access. A scope is not an ownership or sharing grant.\n\nFor delegated REST grants, Port OS verifies token signature, expiry, issuer, REST audience, access-token type and active grant, then checks the operation's scope before existing service resource-policy evaluation. MCP tokens have a separate /mcp audience and cannot be reused on REST. A refresh token is never an API bearer credential.\n\n| Task | Scope |\n| --- | --- |\n| Enumerate or query endpoints/routes | endpoint:read |\n| Modify endpoints/routes | endpoint:write |\n| Send a device message | message:send |\n| Read groups and relationships | group:read |\n| Manage group membership | group:manage |\n| Read subscriptions | subscription:read |\n| Create/update/delete subscriptions | subscription:write |\n\nOther operations are outside the anonymous DCR device profile and fail closed. [Operation pages](/docs/for-ai/operations/index.md) expose exact credential alternatives and resource requirements. [Machine-readable authorization metadata](/docs/references/authorization.yaml) links the contract.\n\nThe existing resource engine converts principal, action, resource and entity data into Cedar requests. Default owner rules and applicable sharing/conditional rules are evaluated; only an Allow outcome admits access. The typed operation IDs come from Port OS's policy action catalog, not invented dotted action names. Owning an endpoint does not override a delegated token's missing scope.\n\nA token with endpoint:read can enumerate permitted lights but cannot dispatch a command. Adding message:send permits dispatch only to endpoints that the signed-in principal's rules allow. An authorized light does not imply camera access. A generic power-capable plug is not automatically a light. For a group request, evaluate the selected targets and report denied or unresolved members separately.\n\nAn invalid/expired/revoked token needs renewed authentication. A scope denial requires the user to approve the needed scope. A resource denial requires the owner to grant the appropriate resource/action access. Never fix denial by switching principal, requesting unrestricted credentials or retrying blindly. Provider authentication failures can require account relinking; they are different from Port OS policy denial. Keep inaccessible resource identities out of diagnostics.\n\nSee [troubleshooting](/docs/for-ai/guides/troubleshooting.md).\n\nCanonical component: [Scopes and authorization rules](/docs/for-ai/guides/authorization-rules.md).\n",
|
|
11
|
+
"content": "# Authentication and dynamic client registration\n\nAll supported agents use the same OAuth components. REST and MCP select different protected resources; DCR and CIMD select different registration methods. Neither product branding nor registration grants device access.\n\nChoose one reading path. This complete walkthrough already includes discovery, registration, consent, token lifecycle and authorization rules below; you do not need to fetch those component links again. If your connector already implements some steps, read only the missing components instead. Both paths use the same canonical instructions.\n\nFor REST, use the discovered issuer as the OAuth resource. For MCP, use the MCP protected-resource metadata. Keep that choice through authorization, exchange and refresh. If you chose the wrong transport, start a new authorization flow for the correct resource; refresh does not switch audiences.\n\nWalkthrough steps (the linked components are also included below):\n\n1. [Discover endpoints and choose REST or MCP](/docs/for-ai/guides/oauth-discovery.md).\n2. [Register with DCR or supported CIMD](/docs/for-ai/guides/oauth-client-registration.md). Remote callbacks require HTTPS; local callback registrations contain only localhost/loopback callbacks. Repeated canonical fingerprints reuse the original public client without expanding scopes.\n3. [Obtain user consent and validate the callback](/docs/for-ai/guides/oauth-consent-and-callback.md). PKCE S256 is required. Pause for the user to sign in and approve access.\n4. [Exchange, refresh and revoke tokens](/docs/for-ai/guides/oauth-token-lifecycle.md). Keep the same canonical resource through authorization, exchange and refresh.\n5. [Verify the connection](/docs/for-ai/guides/first-setup.md), then use [the REST quickstart](/docs/for-ai/guides/quickstart.md) or your client's MCP tools.\n\nRead [authorization rules](/docs/for-ai/guides/authorization-rules.md) for scopes, principal/resource policies, sharing and denial diagnosis. Never ask for a Google password, paste credentials into chat, or approve the agent's own grant. Claimed client names such as Muse and Grok remain unverified.\n\n## Discover the OAuth server and choose REST or MCP\n\nThis discovery step is shared by every client. The deployed pilot API is https://api.portoperatingsystem.lol. Fetch its /.well-known/oauth-authorization-server document anonymously. Read issuer, authorization_endpoint, token_endpoint, registration_endpoint, revocation_endpoint and scopes_supported. Use the returned endpoints exactly. An alias can advertise a different canonical Cloud Run issuer; do not substitute the alias into token audiences.\n\n| Interface | API destination | OAuth resource |\n| --- | --- | --- |\n| REST | The returned issuer, plus the documented operation path | The exact returned issuer |\n| MCP | The returned issuer plus /mcp, Streamable HTTP | The resource returned by /.well-known/oauth-protected-resource/mcp, normally issuer plus /mcp |\n\nAn MCP connector can start at https://api.portoperatingsystem.lol/mcp and follow its WWW-Authenticate resource_metadata URL. Fetch that public metadata and the authorization server it names. MCP initialization alone does not prove that the account's devices are accessible. Never use a REST token on MCP or an MCP token on REST.\n\nChoose the transport before authorizing. Keep its resource unchanged through authorization, token exchange and refresh. If a REST request fails because you authorized the MCP resource (or the reverse), obtain a new user authorization for the correct resource. Refresh cannot change the token's audience.\n\nChoose one supported [client registration method](/docs/for-ai/guides/oauth-client-registration.md), then follow the same [consent and callback steps](/docs/for-ai/guides/oauth-consent-and-callback.md). If metadata omits registration_endpoint, new DCR registration is unavailable; do not guess a replacement endpoint or silently switch to another client's credentials.\n\nSee [scopes and resource rules](/docs/for-ai/guides/authorization-rules.md). Authorization-server scopes_supported describes server vocabulary; a particular client's accepted scopes can be narrower.\n\n### Carry discovery values into every request\n\nThis JavaScript fragment initializes a REST connection. Keep `authorizationServer` and `resource` for the consent and token examples; the API alias is only the discovery starting point.\n\n```javascript\nconst response = await fetch('https://api.portoperatingsystem.lol/.well-known/oauth-authorization-server');\nif (!response.ok) throw new Error('OAuth discovery failed');\nconst authorizationServer = await response.json();\nconst resource = authorizationServer.issuer;\nconst apiBase = authorizationServer.issuer;\n```\n\nFor MCP, assign `resource` from the protected-resource metadata's `resource` field instead. Do not replace either value with the hostname you started from. A wrong `resource` can produce an `invalid_request` error callback even when client registration succeeded.\n\nCanonical component: [Discover the OAuth server and choose REST or MCP](/docs/for-ai/guides/oauth-discovery.md).\n\n## Register an OAuth client with DCR or CIMD\n\nStart with [discovery](/docs/for-ai/guides/oauth-discovery.md). DCR and CIMD identify a client; they do not sign in a user or grant device access. Both use the same [PKCE consent and callback flow](/docs/for-ai/guides/oauth-consent-and-callback.md) and [token lifecycle](/docs/for-ai/guides/oauth-token-lifecycle.md).\n\n### DCR: public clients\n\nPOST application/json to the discovered registration_endpoint:\n\n```json\n{\"client_name\":\"My device assistant\",\"redirect_uris\":[\"https://your-client.example/oauth/callback\"],\"token_endpoint_auth_method\":\"none\",\"grant_types\":[\"authorization_code\",\"refresh_token\"],\"response_types\":[\"code\"],\"scope\":\"endpoint:read message:send\"}\n```\n\nUse a callback the client controls. Remote callbacks require HTTPS. Local clients may use exact HTTP callbacks on localhost or literal loopback IPs, such as http://127.0.0.1:8765/callback. All callbacks in a registration must be local or all must be remote; these sets cannot be mixed. No fragments or user information are permitted. Start the local callback listener before authorization. The website's client-management interface enforces this same callback-set policy.\n\nSuccess is 201 with client_id, client_id_issued_at and accepted metadata. Public clients get no client_secret. Supported DCR scopes are endpoint:read, endpoint:write, message:read, message:send, group:read, group:manage, subscription:read and subscription:write. The default is endpoint:read. Request only the task's necessary scopes and check the returned scope. Broader static-client scopes are outside this anonymous profile.\n\nThe registration fingerprint is SHA-256 of compact canonical JSON with sorted object keys and sorted redirect_uris, grant_types and response_types arrays, plus client_name and token_endpoint_auth_method. Defaults are applied before hashing. Array ordering does not create another client. A matching registration returns the original client ID, issue time, scopes and descriptive metadata. It never overwrites the existing client or expands scopes, even if scope or software_id differs in the new request. Fingerprinting is deduplication, not proof of callback ownership or vendor identity.\n\nDefault quotas are 1,000 new registrations per rolling day and 10,000 retained registrations. Reuse does not consume new-registration quota. A quota failure returns 429 with Retry-After. An operator can disable new registrations without retiring existing grants. Read the [registration operation](/docs/for-ai/operations/registerDynamicOAuthClient.md) for exact request, response and error schemas.\n\n### CIMD: allowlisted metadata clients\n\nA supported CIMD client uses its allowlisted HTTPS metadata URL as client_id instead of posting a DCR request. Port OS retrieves and validates that client metadata, including its exact callbacks and public PKCE profile. Arbitrary metadata URLs and redirects from the metadata fetch are not supported. If your metadata URL is not allowlisted, arrange onboarding or use DCR if your client supports it. Do not pretend another client's metadata URL belongs to you.\n\nClient_name, software_id, software_version and client_uri remain unverified claims. No trusted software-statement issuer is configured; software_statement is rejected. A registration called Muse or Grok does not establish a verified vendor integration or gain additional permissions. Static confidential clients retain their separately configured client authentication method.\n\nCanonical component: [Register an OAuth client with DCR or CIMD](/docs/for-ai/guides/oauth-client-registration.md).\n\n## Obtain user consent and validate the OAuth callback\n\nUse the endpoints and resource from [discovery](/docs/for-ai/guides/oauth-discovery.md) and the client ID and exact callback from [registration](/docs/for-ai/guides/oauth-client-registration.md). These steps are identical for DCR and CIMD, REST and MCP; the chosen resource is the transport-specific input.\n\n1. Start the callback handler the client controls. For a loopback callback, bind its listener locally before opening authorization.\n2. Generate a cryptographically random PKCE verifier of 43–128 permitted characters. Compute code_challenge = BASE64URL(SHA256(verifier)), without padding. Generate a separate random state and retain both in this authorization attempt.\n3. Open the discovered authorization_endpoint with the following query parameters. Percent-encode parameter values; do not append raw callback URLs or scopes into an unescaped query.\n\n```text\nclient_id=<registered ID or supported CIMD metadata URL>\nredirect_uri=<exact registered callback>\nresponse_type=code\nscope=<requested subset of the client's accepted scopes>\nresource=<chosen canonical REST or MCP resource>\nstate=<random client state>\ncode_challenge=<S256 challenge>\ncode_challenge_method=S256\n```\n\nFor REST, use the `resource = authorizationServer.issuer` value retained from discovery. Construct the URL rather than writing an alias into a template. This JavaScript fragment assumes `clientId`, `redirectUri` and `acceptedScope` come from your registration response and callback configuration:\n\n```javascript\nconst base64url = bytes => btoa(String.fromCharCode(...bytes))\n .replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '');\nconst verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));\nconst state = base64url(crypto.getRandomValues(new Uint8Array(32)));\nconst challenge = base64url(new Uint8Array(await crypto.subtle.digest(\n 'SHA-256', new TextEncoder().encode(verifier))));\nconst authorizationURL = new URL(authorizationServer.authorization_endpoint);\nauthorizationURL.search = new URLSearchParams({\n client_id: clientId, redirect_uri: redirectUri, response_type: 'code',\n scope: acceptedScope, resource, state,\n code_challenge: challenge, code_challenge_method: 'S256',\n}).toString();\n// Retain verifier, state and authorizationServer.issuer privately until callback validation.\n// Open authorizationURL.href in the user's browser; wait for their consent.\n```\n\nThe user signs in on Port OS and reviews the requested permissions. Pause for the user; an agent must not approve its own grant, request passwords or manufacture a production sign-in identity. Local functional tests substitute an existing signed fixture identity at this boundary without changing production sign-in.\n\nAfter approval, Port OS navigates to the exact callback with code, state and iss. Reject a callback whose state or issuer does not match this attempt. Treat an error callback as a denied or failed authorization, not a code. Codes and pending consent expire after ten minutes and are single-use. Do not paste codes or credentials into shared chat or logs. Continue with [code exchange and token lifecycle](/docs/for-ai/guides/oauth-token-lifecycle.md).\n\nCanonical component: [Obtain user consent and validate the OAuth callback](/docs/for-ai/guides/oauth-consent-and-callback.md).\n\n## Exchange, refresh and revoke scoped OAuth tokens\n\nComplete [callback validation](/docs/for-ai/guides/oauth-consent-and-callback.md) first. For a public DCR/CIMD client, POST application/x-www-form-urlencoded to the discovered token_endpoint with:\n\n```text\ngrant_type=authorization_code\nclient_id=<client ID used for authorization>\ncode=<validated callback code>\nredirect_uri=<the same exact callback>\ncode_verifier=<this attempt's original verifier>\nresource=<the same canonical resource used for authorization>\n```\n\nContinue with the same discovery values and verifier from the consent example. `code` below must be from a callback whose `state` and `iss` you validated; abort on any callback `error`:\n\n```javascript\nconst tokenResponse = await fetch(authorizationServer.token_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code', client_id: clientId, code,\n redirect_uri: redirectUri, code_verifier: verifier, resource,\n }),\n});\nif (!tokenResponse.ok) throw new Error('OAuth code exchange failed');\nconst tokens = await tokenResponse.json(); // Store securely; do not print tokens.\n```\n\nSave access_token, refresh_token and returned scope in the connector's secure credential store. Public clients have no client secret. Use Authorization: Bearer <access_token> with REST requests or the MCP HTTP connection, according to that token's resource. A refresh token is never an API credential. [Scopes bound access; resource policies still apply](/docs/for-ai/guides/authorization-rules.md).\n\nRefresh with grant_type=refresh_token, client_id, refresh_token and the original resource at token_endpoint. Scope may stay the same or narrow; it cannot expand. Credentials rotate: store the newly returned refresh token atomically and stop using the previous one. A revoked or reused refresh token must not be retried in a loop. Reauthorize when the grant no longer permits the intended operation.\n\nRevoke by POSTing application/x-www-form-urlencoded token=<refresh_token>&client_id=<client_id> to revocation_endpoint. Revoked grants stop authorizing both access and refresh requests. Removing a local configuration file alone is not server-side revocation.\n\nRead [the token operation](/docs/for-ai/operations/generateToken.md), [revocation operation](/docs/for-ai/operations/revokeOAuthGrant.md), and [connection recovery](/docs/for-ai/guides/troubleshooting.md) for schemas and failures. Verify account access using [first setup](/docs/for-ai/guides/first-setup.md), then perform only the customer's intended task. Dispatch acceptance is distinct from observed device state.\n\nCanonical component: [Exchange, refresh and revoke scoped OAuth tokens](/docs/for-ai/guides/oauth-token-lifecycle.md).\n\n## Scopes and authorization rules\n\nAuthentication establishes the principal and client. OAuth scopes bound the user's delegated authority; resource rules decide which selected resources and actions that principal may access. A scope is not an ownership or sharing grant.\n\nFor delegated REST grants, Port OS verifies token signature, expiry, issuer, REST audience, access-token type and active grant, then checks the operation's scope before existing service resource-policy evaluation. MCP tokens have a separate /mcp audience and cannot be reused on REST. A refresh token is never an API bearer credential.\n\n| Task | Scope |\n| --- | --- |\n| Enumerate or query endpoints/routes | endpoint:read |\n| Modify endpoints/routes | endpoint:write |\n| Send a device message | message:send |\n| Read groups and relationships | group:read |\n| Manage group membership | group:manage |\n| Read subscriptions | subscription:read |\n| Create/update/delete subscriptions | subscription:write |\n\nOther operations are outside the anonymous DCR device profile and fail closed. [Operation pages](/docs/for-ai/operations/index.md) expose exact credential alternatives and resource requirements. [Machine-readable authorization metadata](/docs/references/authorization.yaml) links the contract.\n\nThe existing resource engine converts principal, action, resource and entity data into Cedar requests. Default owner rules and applicable sharing/conditional rules are evaluated; only an Allow outcome admits access. The typed operation IDs come from Port OS's policy action catalog, not invented dotted action names. Owning an endpoint does not override a delegated token's missing scope.\n\nA token with endpoint:read can enumerate permitted lights but cannot dispatch a command. Adding message:send permits dispatch only to endpoints that the signed-in principal's rules allow. An authorized light does not imply camera access. A generic power-capable plug is not automatically a light. For a group request, evaluate the selected targets and report denied or unresolved members separately.\n\nAn invalid/expired/revoked token needs renewed authentication. A scope denial requires the user to approve the needed scope. A resource denial requires the owner to grant the appropriate resource/action access. Never fix denial by switching principal, requesting unrestricted credentials or retrying blindly. Provider authentication failures can require account relinking; they are different from Port OS policy denial. Keep inaccessible resource identities out of diagnostics.\n\nSee [troubleshooting](/docs/for-ai/guides/troubleshooting.md).\n\nCanonical component: [Scopes and authorization rules](/docs/for-ai/guides/authorization-rules.md).\n",
|
|
12
12
|
"setupPrompt": null
|
|
13
13
|
},
|
|
14
14
|
{
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
{
|
|
27
27
|
"id": "connect-agent",
|
|
28
28
|
"title": "Connect any supported agent to Port OS",
|
|
29
|
-
"content": "# Connect any supported agent to Port OS\n\nUse these common components for Muse, Grok, Claude, a custom REST agent or an MCP connector. The product name does not change the authorization protocol or permissions. Check your client's current capabilities before assuming it supports a particular registration method.\n\nCopy this prompt into a client that can retrieve public documents:\n\n> Read https://app.portoperatingsystem.lol/docs/for-ai/guides/connect-agent.md. Discover Port OS authentication, choose REST or MCP and a registration method supported by this client, and help me connect. Pause for me to sign in and approve the requested access, then list only devices I can access. Do not claim device control until its result is observed.\n\n| Component | Input | Result |\n| --- | --- | --- |\n| [Discovery and transport](/docs/for-ai/guides/oauth-discovery.md) | Public API/MCP URL | Canonical issuer, endpoints and REST/MCP resource |\n| [DCR or CIMD registration](/docs/for-ai/guides/oauth-client-registration.md) | Client-supported method and controlled callback | Client identity and accepted profile, without device access |\n| [User consent and callback](/docs/for-ai/guides/oauth-consent-and-callback.md) | Client ID, callback, resource and least scopes | Validated one-use authorization code |\n| [Token lifecycle](/docs/for-ai/guides/oauth-token-lifecycle.md) | Code and original PKCE verifier | Scoped access, refresh rotation and revocation |\n| [Authorization rules](/docs/for-ai/guides/authorization-rules.md) | Token scopes and customer's permissions | Effective authority and an explanation of denials |\n| [First setup](/docs/for-ai/guides/first-setup.md) | Authorized connection | Device discovery and one safe verification |\n\nAn MCP-capable client normally manages OAuth and secure token storage itself; supply the MCP URL and complete its consent flow. A REST agent needs an execution environment with HTTP requests, a real callback listener, cryptographic random generation and secure token storage. If the client cannot receive a callback or securely retain credentials, explain the missing capability instead of inventing a bypass.\n\nClient-specific screens can provide UI settings or support notes and link to this table. They must not maintain their own copies of DCR, PKCE, scope, callback or revocation instructions. Actual client/version compatibility and vendor identity require separate validation; the shared guide makes no verified Muse/Grok claim.\n",
|
|
29
|
+
"content": "# Connect any supported agent to Port OS\n\nUse these common components for Muse, Grok, Claude, a custom REST agent or an MCP connector. The product name does not change the authorization protocol or permissions. Check your client's current capabilities before assuming it supports a particular registration method.\n\nFor a new REST connection, read the [complete authentication walkthrough](/docs/for-ai/guides/authentication-and-dcr.md), then the [REST quickstart](/docs/for-ai/guides/quickstart.md). The walkthrough includes the OAuth components in the table below; you can skip their separate files. For an existing connector, use the table to read only the component you need. This directory is a set of alternatives, not a checklist of required downloads.\n\nCopy this prompt into a client that can retrieve public documents:\n\n> Read https://app.portoperatingsystem.lol/docs/for-ai/guides/connect-agent.md. Discover Port OS authentication, choose REST or MCP and a registration method supported by this client, and help me connect. Pause for me to sign in and approve the requested access, then list only devices I can access. Do not claim device control until its result is observed.\n\n| Component | Input | Result |\n| --- | --- | --- |\n| [Discovery and transport](/docs/for-ai/guides/oauth-discovery.md) | Public API/MCP URL | Canonical issuer, endpoints and REST/MCP resource |\n| [DCR or CIMD registration](/docs/for-ai/guides/oauth-client-registration.md) | Client-supported method and controlled callback | Client identity and accepted profile, without device access |\n| [User consent and callback](/docs/for-ai/guides/oauth-consent-and-callback.md) | Client ID, callback, resource and least scopes | Validated one-use authorization code |\n| [Token lifecycle](/docs/for-ai/guides/oauth-token-lifecycle.md) | Code and original PKCE verifier | Scoped access, refresh rotation and revocation |\n| [Authorization rules](/docs/for-ai/guides/authorization-rules.md) | Token scopes and customer's permissions | Effective authority and an explanation of denials |\n| [First setup](/docs/for-ai/guides/first-setup.md) | Authorized connection | Device discovery and one safe verification |\n\nAn MCP-capable client normally manages OAuth and secure token storage itself; supply the MCP URL and complete its consent flow. A REST agent needs an execution environment with HTTP requests, a real callback listener, cryptographic random generation and secure token storage. If the client cannot receive a callback or securely retain credentials, explain the missing capability instead of inventing a bypass.\n\nClient-specific screens can provide UI settings or support notes and link to this table. They must not maintain their own copies of DCR, PKCE, scope, callback or revocation instructions. Actual client/version compatibility and vendor identity require separate validation; the shared guide makes no verified Muse/Grok claim.\n",
|
|
30
30
|
"setupPrompt": "Read https://app.portoperatingsystem.lol/docs/for-ai/guides/connect-agent.md. Discover Port OS authentication, choose REST or MCP and a registration method supported by this client, and help me connect. Pause for me to sign in and approve the requested access, then list only devices I can access. Do not claim device control until its result is observed."
|
|
31
31
|
},
|
|
32
32
|
{
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
{
|
|
87
87
|
"id": "oauth-discovery",
|
|
88
88
|
"title": "Discover the OAuth server and choose REST or MCP",
|
|
89
|
-
"content": "# Discover the OAuth server and choose REST or MCP\n\nThis discovery step is shared by every client. The deployed pilot API is https://api.portoperatingsystem.lol. Fetch its /.well-known/oauth-authorization-server document anonymously. Read issuer, authorization_endpoint, token_endpoint, registration_endpoint, revocation_endpoint and scopes_supported. Use the returned endpoints exactly. An alias can advertise a different canonical Cloud Run issuer; do not substitute the alias into token audiences.\n\n| Interface | API destination | OAuth resource |\n| --- | --- | --- |\n| REST | The returned issuer, plus the documented operation path | The exact returned issuer |\n| MCP | The returned issuer plus /mcp, Streamable HTTP | The resource returned by /.well-known/oauth-protected-resource/mcp, normally issuer plus /mcp |\n\nAn MCP connector can start at https://api.portoperatingsystem.lol/mcp and follow its WWW-Authenticate resource_metadata URL. Fetch that public metadata and the authorization server it names. MCP initialization alone does not prove that the account's devices are accessible. Never use a REST token on MCP or an MCP token on REST.\n\nChoose one supported [client registration method](/docs/for-ai/guides/oauth-client-registration.md), then follow the same [consent and callback steps](/docs/for-ai/guides/oauth-consent-and-callback.md). If metadata omits registration_endpoint, new DCR registration is unavailable; do not guess a replacement endpoint or silently switch to another client's credentials.\n\nSee [scopes and resource rules](/docs/for-ai/guides/authorization-rules.md). Authorization-server scopes_supported describes server vocabulary; a particular client's accepted scopes can be narrower.\n\n## Carry discovery values into every request\n\nThis JavaScript fragment initializes a REST connection. Keep `authorizationServer` and `resource` for the consent and token examples; the API alias is only the discovery starting point.\n\n```javascript\nconst response = await fetch('https://api.portoperatingsystem.lol/.well-known/oauth-authorization-server');\nif (!response.ok) throw new Error('OAuth discovery failed');\nconst authorizationServer = await response.json();\nconst resource = authorizationServer.issuer;\nconst apiBase = authorizationServer.issuer;\n```\n\nFor MCP, assign `resource` from the protected-resource metadata's `resource` field instead. Do not replace either value with the hostname you started from. A wrong `resource` can produce an `invalid_request` error callback even when client registration succeeded.\n",
|
|
89
|
+
"content": "# Discover the OAuth server and choose REST or MCP\n\nThis discovery step is shared by every client. The deployed pilot API is https://api.portoperatingsystem.lol. Fetch its /.well-known/oauth-authorization-server document anonymously. Read issuer, authorization_endpoint, token_endpoint, registration_endpoint, revocation_endpoint and scopes_supported. Use the returned endpoints exactly. An alias can advertise a different canonical Cloud Run issuer; do not substitute the alias into token audiences.\n\n| Interface | API destination | OAuth resource |\n| --- | --- | --- |\n| REST | The returned issuer, plus the documented operation path | The exact returned issuer |\n| MCP | The returned issuer plus /mcp, Streamable HTTP | The resource returned by /.well-known/oauth-protected-resource/mcp, normally issuer plus /mcp |\n\nAn MCP connector can start at https://api.portoperatingsystem.lol/mcp and follow its WWW-Authenticate resource_metadata URL. Fetch that public metadata and the authorization server it names. MCP initialization alone does not prove that the account's devices are accessible. Never use a REST token on MCP or an MCP token on REST.\n\nChoose the transport before authorizing. Keep its resource unchanged through authorization, token exchange and refresh. If a REST request fails because you authorized the MCP resource (or the reverse), obtain a new user authorization for the correct resource. Refresh cannot change the token's audience.\n\nChoose one supported [client registration method](/docs/for-ai/guides/oauth-client-registration.md), then follow the same [consent and callback steps](/docs/for-ai/guides/oauth-consent-and-callback.md). If metadata omits registration_endpoint, new DCR registration is unavailable; do not guess a replacement endpoint or silently switch to another client's credentials.\n\nSee [scopes and resource rules](/docs/for-ai/guides/authorization-rules.md). Authorization-server scopes_supported describes server vocabulary; a particular client's accepted scopes can be narrower.\n\n## Carry discovery values into every request\n\nThis JavaScript fragment initializes a REST connection. Keep `authorizationServer` and `resource` for the consent and token examples; the API alias is only the discovery starting point.\n\n```javascript\nconst response = await fetch('https://api.portoperatingsystem.lol/.well-known/oauth-authorization-server');\nif (!response.ok) throw new Error('OAuth discovery failed');\nconst authorizationServer = await response.json();\nconst resource = authorizationServer.issuer;\nconst apiBase = authorizationServer.issuer;\n```\n\nFor MCP, assign `resource` from the protected-resource metadata's `resource` field instead. Do not replace either value with the hostname you started from. A wrong `resource` can produce an `invalid_request` error callback even when client registration succeeded.\n",
|
|
90
90
|
"setupPrompt": null
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
{
|
|
105
105
|
"id": "quickstart",
|
|
106
106
|
"title": "Verify the connection and control one light",
|
|
107
|
-
"content": "# Verify the connection and control one light\n\nFirst complete [OAuth setup](/docs/for-ai/guides/authentication-and-dcr.md). Keep the discovered API issuer and credentials in client configuration.\n\n1. GET /endpoints?owner=~caller&maxResults=25 with Authorization: Bearer <access_token>. Read [enumerateEndpoints](/docs/for-ai/operations/enumerateEndpoints.md) for the exact pagination and response fields. A filter such as owner is required. Follow the returned continuation metadata until the authorized enumeration is complete. An empty list means valid access with no visible devices; guide the user to [add devices](/docs/for-ai/guides/adding-devices.md).\n2. Select one light by full endpoint ID and group/room context. If the target is ambiguous, ask the user before acting.\n3. Inspect its declared capability namespace and version. A light declaring power 1.0 uses the [power guide](/docs/for-ai/capability-interfaces/power/1.0.md) and [turn-on payload schema](/docs/references/capability-interfaces/power/1.0/messages/turn-on.yaml). For other versions or capabilities, follow [the capability directory](/docs/for-ai/capability-interfaces.md).\n4. With the user's intended action, POST /messages. Read [sendMessage](/docs/for-ai/operations/sendMessage.md) and its linked request schema. Use the endpoint ID and message version you discovered, not the illustrative ID below.\n\n```json\n{\"target\":{\"type\":\"ENDPOINT\",\"id\":\"port1/principals/example/endpoints/example-light\"},\"message\":{\"header\":{\"namespace\":\"port1/systems/zero/capability-interfaces/power\",\"name\":\"turn-on\",\"version\":\"1.0\"},\"body\":{}}}\n```\n\
|
|
107
|
+
"content": "# Verify the connection and control one light\n\nFirst complete [OAuth setup](/docs/for-ai/guides/authentication-and-dcr.md). Keep the discovered API issuer and credentials in client configuration.\n\n1. GET /endpoints?owner=~caller&maxResults=25 with Authorization: Bearer <access_token>. Read [enumerateEndpoints](/docs/for-ai/operations/enumerateEndpoints.md) for the exact pagination and response fields. A filter such as owner is required. Follow the returned continuation metadata until the authorized enumeration is complete. An empty list means valid access with no visible devices; guide the user to [add devices](/docs/for-ai/guides/adding-devices.md).\n2. Select one light by full endpoint ID and group/room context. If the target is ambiguous, ask the user before acting.\n3. Inspect its declared capability namespace and version. A light declaring power 1.0 uses the [power guide](/docs/for-ai/capability-interfaces/power/1.0.md) and [turn-on payload schema](/docs/references/capability-interfaces/power/1.0/messages/turn-on.yaml). For other versions or capabilities, follow [the capability directory](/docs/for-ai/capability-interfaces.md).\n4. With the user's intended action, POST /messages with Authorization: Bearer <access_token> and Content-Type: application/json. Send the request object below as the JSON body, without a surrounding `body` field or a JSON string. Read [sendMessage](/docs/for-ai/operations/sendMessage.md) and its linked request schema. Use the endpoint ID and message version you discovered, not the illustrative ID below.\n\n```json\n{\"target\":{\"type\":\"ENDPOINT\",\"id\":\"port1/principals/example/endpoints/example-light\"},\"message\":{\"header\":{\"namespace\":\"port1/systems/zero/capability-interfaces/power\",\"name\":\"turn-on\",\"version\":\"1.0\"},\"body\":{}}}\n```\n\nHTTP 200 acknowledges acceptance. An asynchronous request can return `{\"messageId\":\"\"}` without a `message`; the empty ID is valid and does not provide a durable receipt or retry deduplication. When a unary response message is present, `messageId` identifies that response. Neither response proves physical state. After a lost response, treat the outcome as unknown and inspect state before deciding whether another action is safe.\n\nDo not cut JSON with head -c. GET /endpoints?id=<URL-encoded-endpoint-id>&expand=interfaces.attributes&forceDeviceAttributeQuery=true to request permitted device-backed state. Inspect attributes and partial errors. Check sampledAt against the command time and the capability version. sampledAt is the source-reported time, not the fetch time; an absent timestamp means freshness is unknown. Use [enumerateEndpoints and its linked components](/docs/for-ai/operations/enumerateEndpoints.md) for exact response shapes. Report returned device state separately from dispatch acceptance. Stale/unavailable state means unknown, and user confirmation may be necessary for a physical device.\n\nFor first setup, test one explicitly selected safe light. Do not turn off all homes as a connectivity check. See [lighting](/docs/for-ai/guides/lighting.md), [first setup](/docs/for-ai/guides/first-setup.md) and [authorization rules](/docs/for-ai/guides/authorization-rules.md).\n",
|
|
108
108
|
"setupPrompt": null
|
|
109
109
|
},
|
|
110
110
|
{
|
package/generated/openapi.json
CHANGED
|
@@ -9826,14 +9826,14 @@
|
|
|
9826
9826
|
},
|
|
9827
9827
|
"responses": {
|
|
9828
9828
|
"200": {
|
|
9829
|
-
"description": "
|
|
9829
|
+
"description": "Request accepted; an asynchronous acknowledgment may have an empty messageId and no message. Verify device state separately.",
|
|
9830
9830
|
"content": {
|
|
9831
9831
|
"application/json": {
|
|
9832
9832
|
"schema": {
|
|
9833
9833
|
"$ref": "#/components/schemas/CreateMessageResponse"
|
|
9834
9834
|
},
|
|
9835
9835
|
"example": {
|
|
9836
|
-
"messageId": "
|
|
9836
|
+
"messageId": ""
|
|
9837
9837
|
}
|
|
9838
9838
|
}
|
|
9839
9839
|
}
|
|
@@ -10106,14 +10106,14 @@
|
|
|
10106
10106
|
},
|
|
10107
10107
|
"responses": {
|
|
10108
10108
|
"200": {
|
|
10109
|
-
"description": "
|
|
10109
|
+
"description": "Request accepted; an asynchronous acknowledgment may have an empty messageId and no message. Verify device state separately.",
|
|
10110
10110
|
"content": {
|
|
10111
10111
|
"application/json": {
|
|
10112
10112
|
"schema": {
|
|
10113
10113
|
"$ref": "#/components/schemas/CreateMessageResponse"
|
|
10114
10114
|
},
|
|
10115
10115
|
"example": {
|
|
10116
|
-
"messageId": "
|
|
10116
|
+
"messageId": ""
|
|
10117
10117
|
}
|
|
10118
10118
|
}
|
|
10119
10119
|
}
|
|
@@ -26762,7 +26762,7 @@
|
|
|
26762
26762
|
"properties": {
|
|
26763
26763
|
"messageId": {
|
|
26764
26764
|
"type": "string",
|
|
26765
|
-
"description": "
|
|
26765
|
+
"description": "Identifies the unary response message when one is returned. An accepted asynchronous request can return an empty string with no message. This field does not provide a durable dispatch receipt, retry deduplication or proof of observed device state."
|
|
26766
26766
|
},
|
|
26767
26767
|
"message": {
|
|
26768
26768
|
"$ref": "#/components/schemas/Message",
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
All supported agents use the same OAuth components. REST and MCP select different protected resources; DCR and CIMD select different registration methods. Neither product branding nor registration grants device access.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Choose one reading path. This complete walkthrough already includes discovery, registration, consent, token lifecycle and authorization rules below; you do not need to fetch those component links again. If your connector already implements some steps, read only the missing components instead. Both paths use the same canonical instructions.
|
|
6
|
+
|
|
7
|
+
For REST, use the discovered issuer as the OAuth resource. For MCP, use the MCP protected-resource metadata. Keep that choice through authorization, exchange and refresh. If you chose the wrong transport, start a new authorization flow for the correct resource; refresh does not switch audiences.
|
|
8
|
+
|
|
9
|
+
Walkthrough steps (the linked components are also included below):
|
|
6
10
|
|
|
7
11
|
1. [Discover endpoints and choose REST or MCP](/docs/for-ai/guides/oauth-discovery.md).
|
|
8
12
|
2. [Register with DCR or supported CIMD](/docs/for-ai/guides/oauth-client-registration.md). Remote callbacks require HTTPS; local callback registrations contain only localhost/loopback callbacks. Repeated canonical fingerprints reuse the original public client without expanding scopes.
|
|
@@ -23,6 +27,8 @@ This discovery step is shared by every client. The deployed pilot API is https:/
|
|
|
23
27
|
|
|
24
28
|
An MCP connector can start at https://api.portoperatingsystem.lol/mcp and follow its WWW-Authenticate resource_metadata URL. Fetch that public metadata and the authorization server it names. MCP initialization alone does not prove that the account's devices are accessible. Never use a REST token on MCP or an MCP token on REST.
|
|
25
29
|
|
|
30
|
+
Choose the transport before authorizing. Keep its resource unchanged through authorization, token exchange and refresh. If a REST request fails because you authorized the MCP resource (or the reverse), obtain a new user authorization for the correct resource. Refresh cannot change the token's audience.
|
|
31
|
+
|
|
26
32
|
Choose one supported [client registration method](/docs/for-ai/guides/oauth-client-registration.md), then follow the same [consent and callback steps](/docs/for-ai/guides/oauth-consent-and-callback.md). If metadata omits registration_endpoint, new DCR registration is unavailable; do not guess a replacement endpoint or silently switch to another client's credentials.
|
|
27
33
|
|
|
28
34
|
See [scopes and resource rules](/docs/for-ai/guides/authorization-rules.md). Authorization-server scopes_supported describes server vocabulary; a particular client's accepted scopes can be narrower.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Use these common components for Muse, Grok, Claude, a custom REST agent or an MCP connector. The product name does not change the authorization protocol or permissions. Check your client's current capabilities before assuming it supports a particular registration method.
|
|
4
4
|
|
|
5
|
+
For a new REST connection, read the [complete authentication walkthrough](/docs/for-ai/guides/authentication-and-dcr.md), then the [REST quickstart](/docs/for-ai/guides/quickstart.md). The walkthrough includes the OAuth components in the table below; you can skip their separate files. For an existing connector, use the table to read only the component you need. This directory is a set of alternatives, not a checklist of required downloads.
|
|
6
|
+
|
|
5
7
|
Copy this prompt into a client that can retrieve public documents:
|
|
6
8
|
|
|
7
9
|
> Read https://app.portoperatingsystem.lol/docs/for-ai/guides/connect-agent.md. Discover Port OS authentication, choose REST or MCP and a registration method supported by this client, and help me connect. Pause for me to sign in and approve the requested access, then list only devices I can access. Do not claim device control until its result is observed.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# User guides
|
|
2
2
|
|
|
3
|
+
This is a directory, not a required reading checklist. For a new REST connection, read [the complete authentication walkthrough](/docs/for-ai/guides/authentication-and-dcr.md) and [the quickstart](/docs/for-ai/guides/quickstart.md). The walkthrough already includes its OAuth components. Read individual components only when you need a specific step.
|
|
4
|
+
|
|
3
5
|
- [Add devices through provider linking](/docs/for-ai/guides/adding-devices.md)
|
|
4
6
|
- [Authentication and dynamic client registration](/docs/for-ai/guides/authentication-and-dcr.md)
|
|
5
7
|
- [Scopes and authorization rules](/docs/for-ai/guides/authorization-rules.md)
|
|
@@ -9,6 +9,8 @@ This discovery step is shared by every client. The deployed pilot API is https:/
|
|
|
9
9
|
|
|
10
10
|
An MCP connector can start at https://api.portoperatingsystem.lol/mcp and follow its WWW-Authenticate resource_metadata URL. Fetch that public metadata and the authorization server it names. MCP initialization alone does not prove that the account's devices are accessible. Never use a REST token on MCP or an MCP token on REST.
|
|
11
11
|
|
|
12
|
+
Choose the transport before authorizing. Keep its resource unchanged through authorization, token exchange and refresh. If a REST request fails because you authorized the MCP resource (or the reverse), obtain a new user authorization for the correct resource. Refresh cannot change the token's audience.
|
|
13
|
+
|
|
12
14
|
Choose one supported [client registration method](/docs/for-ai/guides/oauth-client-registration.md), then follow the same [consent and callback steps](/docs/for-ai/guides/oauth-consent-and-callback.md). If metadata omits registration_endpoint, new DCR registration is unavailable; do not guess a replacement endpoint or silently switch to another client's credentials.
|
|
13
15
|
|
|
14
16
|
See [scopes and resource rules](/docs/for-ai/guides/authorization-rules.md). Authorization-server scopes_supported describes server vocabulary; a particular client's accepted scopes can be narrower.
|
|
@@ -5,12 +5,14 @@ First complete [OAuth setup](/docs/for-ai/guides/authentication-and-dcr.md). Kee
|
|
|
5
5
|
1. GET /endpoints?owner=~caller&maxResults=25 with Authorization: Bearer <access_token>. Read [enumerateEndpoints](/docs/for-ai/operations/enumerateEndpoints.md) for the exact pagination and response fields. A filter such as owner is required. Follow the returned continuation metadata until the authorized enumeration is complete. An empty list means valid access with no visible devices; guide the user to [add devices](/docs/for-ai/guides/adding-devices.md).
|
|
6
6
|
2. Select one light by full endpoint ID and group/room context. If the target is ambiguous, ask the user before acting.
|
|
7
7
|
3. Inspect its declared capability namespace and version. A light declaring power 1.0 uses the [power guide](/docs/for-ai/capability-interfaces/power/1.0.md) and [turn-on payload schema](/docs/references/capability-interfaces/power/1.0/messages/turn-on.yaml). For other versions or capabilities, follow [the capability directory](/docs/for-ai/capability-interfaces.md).
|
|
8
|
-
4. With the user's intended action, POST /messages. Read [sendMessage](/docs/for-ai/operations/sendMessage.md) and its linked request schema. Use the endpoint ID and message version you discovered, not the illustrative ID below.
|
|
8
|
+
4. With the user's intended action, POST /messages with Authorization: Bearer <access_token> and Content-Type: application/json. Send the request object below as the JSON body, without a surrounding `body` field or a JSON string. Read [sendMessage](/docs/for-ai/operations/sendMessage.md) and its linked request schema. Use the endpoint ID and message version you discovered, not the illustrative ID below.
|
|
9
9
|
|
|
10
10
|
```json
|
|
11
11
|
{"target":{"type":"ENDPOINT","id":"port1/principals/example/endpoints/example-light"},"message":{"header":{"namespace":"port1/systems/zero/capability-interfaces/power","name":"turn-on","version":"1.0"},"body":{}}}
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
HTTP 200 acknowledges acceptance. An asynchronous request can return `{"messageId":""}` without a `message`; the empty ID is valid and does not provide a durable receipt or retry deduplication. When a unary response message is present, `messageId` identifies that response. Neither response proves physical state. After a lost response, treat the outcome as unknown and inspect state before deciding whether another action is safe.
|
|
15
|
+
|
|
16
|
+
Do not cut JSON with head -c. GET /endpoints?id=<URL-encoded-endpoint-id>&expand=interfaces.attributes&forceDeviceAttributeQuery=true to request permitted device-backed state. Inspect attributes and partial errors. Check sampledAt against the command time and the capability version. sampledAt is the source-reported time, not the fetch time; an absent timestamp means freshness is unknown. Use [enumerateEndpoints and its linked components](/docs/for-ai/operations/enumerateEndpoints.md) for exact response shapes. Report returned device state separately from dispatch acceptance. Stale/unavailable state means unknown, and user confirmation may be necessary for a physical device.
|
|
15
17
|
|
|
16
18
|
For first setup, test one explicitly selected safe light. Do not turn off all homes as a connectivity check. See [lighting](/docs/for-ai/guides/lighting.md), [first setup](/docs/for-ai/guides/first-setup.md) and [authorization rules](/docs/for-ai/guides/authorization-rules.md).
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
# Port OS: start here for agents
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## New REST connection
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Read [the complete authentication walkthrough](/docs/for-ai/guides/authentication-and-dcr.md), then [the REST quickstart](/docs/for-ai/guides/quickstart.md). The walkthrough includes discovery, DCR/CIMD, consent, callback, token lifecycle and permission rules. You do not need to read their separate component files again. Follow an operation or capability link from the quickstart only when you need its exact contract.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
Registration alone gives no device access. Wait for user consent and validate the callback before exchanging its code with PKCE. Never request the user’s Google password or reuse another client’s tokens.
|
|
8
|
+
|
|
9
|
+
## Existing connector or MCP
|
|
10
|
+
|
|
11
|
+
Use [the shared connection guide](/docs/for-ai/guides/connect-agent.md) to select the transport and only the components your client needs. REST and MCP use different OAuth resources; choose before authorization and retain the resource through exchange and refresh.
|
|
12
|
+
|
|
13
|
+
## Documentation tree: look up what you need
|
|
14
|
+
|
|
15
|
+
- [Task/component guide directory](/docs/for-ai/guides/index.md): alternatives, not a required reading checklist
|
|
16
|
+
- [REST operation directory](/docs/for-ai/operations/index.md): individual operations and their scopes
|
|
17
|
+
- [Small REST schema files](/docs/for-ai/schemas.md): linked request/response components
|
|
18
|
+
- [Capability directory](/docs/for-ai/capability-interfaces.md): match the discovered namespace and version
|
|
19
|
+
- [Authorization inventory](/docs/references/authorization.yaml): machine-readable scope/policy rules
|
|
20
|
+
- [Full OpenAPI YAML](/docs/references/openapi.yaml): comprehensive lookup and SDK generation; unnecessary for the quickstart
|
|
13
21
|
- [Capability YAML index](/docs/references/capability-interfaces.yaml)
|
|
14
|
-
- [Authorization inventory](/docs/references/authorization.yaml)
|
|
15
22
|
- [Release manifest](/docs/references/manifest.json)
|
|
16
23
|
|
|
17
24
|
These links are public UTF-8 documents. You do not need JavaScript, sign-in, or a repository checkout to read them. Device data still requires consent and resource permission.
|
|
@@ -114,13 +114,15 @@ post:
|
|
|
114
114
|
body: {}
|
|
115
115
|
responses:
|
|
116
116
|
'200':
|
|
117
|
-
description:
|
|
117
|
+
description: >-
|
|
118
|
+
Request accepted; an asynchronous acknowledgment may have an empty messageId and no message.
|
|
119
|
+
Verify device state separately.
|
|
118
120
|
content:
|
|
119
121
|
application/json:
|
|
120
122
|
schema:
|
|
121
123
|
$ref: /docs/references/schemas/CreateMessageResponse.json
|
|
122
124
|
example:
|
|
123
|
-
messageId:
|
|
125
|
+
messageId: ''
|
|
124
126
|
'400':
|
|
125
127
|
description: Invalid request payload or plugin dispatch request error
|
|
126
128
|
content:
|
|
@@ -114,13 +114,15 @@ post:
|
|
|
114
114
|
body: {}
|
|
115
115
|
responses:
|
|
116
116
|
'200':
|
|
117
|
-
description:
|
|
117
|
+
description: >-
|
|
118
|
+
Request accepted; an asynchronous acknowledgment may have an empty messageId and no message.
|
|
119
|
+
Verify device state separately.
|
|
118
120
|
content:
|
|
119
121
|
application/json:
|
|
120
122
|
schema:
|
|
121
123
|
$ref: /docs/references/schemas/CreateMessageResponse.json
|
|
122
124
|
example:
|
|
123
|
-
messageId:
|
|
125
|
+
messageId: ''
|
|
124
126
|
'400':
|
|
125
127
|
description: Invalid request payload or plugin dispatch request error
|
|
126
128
|
content:
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "@portpowered/reference-data",
|
|
3
3
|
"goModule": "github.com/portpowered/portos-reference-data",
|
|
4
|
-
"version": "0.4.
|
|
5
|
-
"sourceCommit": "
|
|
4
|
+
"version": "0.4.5",
|
|
5
|
+
"sourceCommit": "07fdcc4ef4f322950ecb617ffaf47a79b9f0a122",
|
|
6
6
|
"sourceInputState": "committed",
|
|
7
|
-
"sourceInputSHA256": "
|
|
7
|
+
"sourceInputSHA256": "d8183268527e9f3de34ec8374b1007a4ed48cc0d4255dd8c3bc5822f10e57818",
|
|
8
8
|
"sourceInputCount": 735,
|
|
9
|
-
"assetSetSHA256": "
|
|
9
|
+
"assetSetSHA256": "bae4942d5ad905c28c49cd3967c8e3c24f54db3dc36c90119208fd9c12ef1adc",
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"path": "/docs/for-ai/capability-interfaces.md",
|
|
@@ -1184,8 +1184,8 @@
|
|
|
1184
1184
|
{
|
|
1185
1185
|
"path": "/docs/for-ai/guides/authentication-and-dcr.md",
|
|
1186
1186
|
"contentType": "text/plain; charset=utf-8",
|
|
1187
|
-
"bytes":
|
|
1188
|
-
"sha256": "
|
|
1187
|
+
"bytes": 17581,
|
|
1188
|
+
"sha256": "32616ea9e28a675efe2c2dee51f149eefec2dead1af2b5275b91c3c9bae3a7be",
|
|
1189
1189
|
"links": [
|
|
1190
1190
|
"/docs/for-ai/guides/authorization-rules.md",
|
|
1191
1191
|
"/docs/for-ai/guides/first-setup.md",
|
|
@@ -1227,15 +1227,17 @@
|
|
|
1227
1227
|
{
|
|
1228
1228
|
"path": "/docs/for-ai/guides/connect-agent.md",
|
|
1229
1229
|
"contentType": "text/plain; charset=utf-8",
|
|
1230
|
-
"bytes":
|
|
1231
|
-
"sha256": "
|
|
1230
|
+
"bytes": 2919,
|
|
1231
|
+
"sha256": "5aa9459297c58a1b648b2385a546b25814567c4bdbaef30ab03ffe909f73d391",
|
|
1232
1232
|
"links": [
|
|
1233
|
+
"/docs/for-ai/guides/authentication-and-dcr.md",
|
|
1233
1234
|
"/docs/for-ai/guides/authorization-rules.md",
|
|
1234
1235
|
"/docs/for-ai/guides/first-setup.md",
|
|
1235
1236
|
"/docs/for-ai/guides/oauth-client-registration.md",
|
|
1236
1237
|
"/docs/for-ai/guides/oauth-consent-and-callback.md",
|
|
1237
1238
|
"/docs/for-ai/guides/oauth-discovery.md",
|
|
1238
|
-
"/docs/for-ai/guides/oauth-token-lifecycle.md"
|
|
1239
|
+
"/docs/for-ai/guides/oauth-token-lifecycle.md",
|
|
1240
|
+
"/docs/for-ai/guides/quickstart.md"
|
|
1239
1241
|
]
|
|
1240
1242
|
},
|
|
1241
1243
|
{
|
|
@@ -1270,8 +1272,8 @@
|
|
|
1270
1272
|
{
|
|
1271
1273
|
"path": "/docs/for-ai/guides/index.md",
|
|
1272
1274
|
"contentType": "text/plain; charset=utf-8",
|
|
1273
|
-
"bytes":
|
|
1274
|
-
"sha256": "
|
|
1275
|
+
"bytes": 2190,
|
|
1276
|
+
"sha256": "936e643766ce6a3c245a03f91b8a1b74087ce4cfa237e6f94e8e0939e92a6720",
|
|
1275
1277
|
"links": [
|
|
1276
1278
|
"/docs/for-ai/guides/adding-devices.md",
|
|
1277
1279
|
"/docs/for-ai/guides/authentication-and-dcr.md",
|
|
@@ -1366,8 +1368,8 @@
|
|
|
1366
1368
|
{
|
|
1367
1369
|
"path": "/docs/for-ai/guides/oauth-discovery.md",
|
|
1368
1370
|
"contentType": "text/plain; charset=utf-8",
|
|
1369
|
-
"bytes":
|
|
1370
|
-
"sha256": "
|
|
1371
|
+
"bytes": 2850,
|
|
1372
|
+
"sha256": "588efd7396d2b5b600eb2bd085d1c947afc429fb5f383cc3093734e9ce2a27c7",
|
|
1371
1373
|
"links": [
|
|
1372
1374
|
"/docs/for-ai/guides/authorization-rules.md",
|
|
1373
1375
|
"/docs/for-ai/guides/oauth-client-registration.md",
|
|
@@ -1401,8 +1403,8 @@
|
|
|
1401
1403
|
{
|
|
1402
1404
|
"path": "/docs/for-ai/guides/quickstart.md",
|
|
1403
1405
|
"contentType": "text/plain; charset=utf-8",
|
|
1404
|
-
"bytes":
|
|
1405
|
-
"sha256": "
|
|
1406
|
+
"bytes": 3180,
|
|
1407
|
+
"sha256": "a230400fa9f48bd5b629bad119bf079cbdf2faabee6270137ae0e9b2fa0b0fce",
|
|
1406
1408
|
"links": [
|
|
1407
1409
|
"/docs/for-ai/capability-interfaces.md",
|
|
1408
1410
|
"/docs/for-ai/capability-interfaces/power/1.0.md",
|
|
@@ -1475,12 +1477,12 @@
|
|
|
1475
1477
|
{
|
|
1476
1478
|
"path": "/docs/for-ai/index.md",
|
|
1477
1479
|
"contentType": "text/plain; charset=utf-8",
|
|
1478
|
-
"bytes":
|
|
1479
|
-
"sha256": "
|
|
1480
|
+
"bytes": 1973,
|
|
1481
|
+
"sha256": "303ac8481dfd46c092f1e9fe988740878d146d0194404b309eeb554f4eb2973c",
|
|
1480
1482
|
"links": [
|
|
1481
1483
|
"/docs/for-ai/capability-interfaces.md",
|
|
1482
1484
|
"/docs/for-ai/guides/authentication-and-dcr.md",
|
|
1483
|
-
"/docs/for-ai/guides/
|
|
1485
|
+
"/docs/for-ai/guides/connect-agent.md",
|
|
1484
1486
|
"/docs/for-ai/guides/index.md",
|
|
1485
1487
|
"/docs/for-ai/guides/quickstart.md",
|
|
1486
1488
|
"/docs/for-ai/operations/index.md",
|
|
@@ -2649,8 +2651,8 @@
|
|
|
2649
2651
|
{
|
|
2650
2652
|
"path": "/docs/for-ai/operations/sendMessage.md",
|
|
2651
2653
|
"contentType": "text/plain; charset=utf-8",
|
|
2652
|
-
"bytes":
|
|
2653
|
-
"sha256": "
|
|
2654
|
+
"bytes": 9743,
|
|
2655
|
+
"sha256": "e9c1092a4809c9298d03d3bf0baec094125ea2f28ce2eddea031d2d8252e4f6d",
|
|
2654
2656
|
"links": [
|
|
2655
2657
|
"/docs/for-ai/guides/authentication-and-dcr.md",
|
|
2656
2658
|
"/docs/for-ai/schemas.md",
|
|
@@ -2662,8 +2664,8 @@
|
|
|
2662
2664
|
{
|
|
2663
2665
|
"path": "/docs/for-ai/operations/sendMessageLegacyAlias.md",
|
|
2664
2666
|
"contentType": "text/plain; charset=utf-8",
|
|
2665
|
-
"bytes":
|
|
2666
|
-
"sha256": "
|
|
2667
|
+
"bytes": 10014,
|
|
2668
|
+
"sha256": "0194b7b87f6ead4e405250f0e3710d89447e7d56e0df8efad9b57f800bf9a8af",
|
|
2667
2669
|
"links": [
|
|
2668
2670
|
"/docs/for-ai/guides/authentication-and-dcr.md",
|
|
2669
2671
|
"/docs/for-ai/schemas.md",
|
|
@@ -7350,8 +7352,8 @@
|
|
|
7350
7352
|
{
|
|
7351
7353
|
"path": "/docs/references/openapi.yaml",
|
|
7352
7354
|
"contentType": "application/yaml; charset=utf-8",
|
|
7353
|
-
"bytes":
|
|
7354
|
-
"sha256": "
|
|
7355
|
+
"bytes": 757609,
|
|
7356
|
+
"sha256": "a6db7cfd7943b4298c07af9dbf1078bcccc15725fa3a4e7a329f92c18e19c89c",
|
|
7355
7357
|
"links": []
|
|
7356
7358
|
},
|
|
7357
7359
|
{
|
|
@@ -8407,8 +8409,8 @@
|
|
|
8407
8409
|
{
|
|
8408
8410
|
"path": "/docs/references/schemas/CreateMessageResponse.json",
|
|
8409
8411
|
"contentType": "application/json",
|
|
8410
|
-
"bytes":
|
|
8411
|
-
"sha256": "
|
|
8412
|
+
"bytes": 798,
|
|
8413
|
+
"sha256": "d97a132f4abb5f70006e0f1340dd04a8d76e954c19b5bb557e86360a68071be5",
|
|
8412
8414
|
"links": []
|
|
8413
8415
|
},
|
|
8414
8416
|
{
|
|
@@ -7136,13 +7136,15 @@ paths:
|
|
|
7136
7136
|
body: {}
|
|
7137
7137
|
responses:
|
|
7138
7138
|
'200':
|
|
7139
|
-
description:
|
|
7139
|
+
description: >-
|
|
7140
|
+
Request accepted; an asynchronous acknowledgment may have an empty messageId and no
|
|
7141
|
+
message. Verify device state separately.
|
|
7140
7142
|
content:
|
|
7141
7143
|
application/json:
|
|
7142
7144
|
schema:
|
|
7143
7145
|
$ref: '#/components/schemas/CreateMessageResponse'
|
|
7144
7146
|
example:
|
|
7145
|
-
messageId:
|
|
7147
|
+
messageId: ''
|
|
7146
7148
|
'400':
|
|
7147
7149
|
description: Invalid request payload or plugin dispatch request error
|
|
7148
7150
|
content:
|
|
@@ -7366,13 +7368,15 @@ paths:
|
|
|
7366
7368
|
body: {}
|
|
7367
7369
|
responses:
|
|
7368
7370
|
'200':
|
|
7369
|
-
description:
|
|
7371
|
+
description: >-
|
|
7372
|
+
Request accepted; an asynchronous acknowledgment may have an empty messageId and no
|
|
7373
|
+
message. Verify device state separately.
|
|
7370
7374
|
content:
|
|
7371
7375
|
application/json:
|
|
7372
7376
|
schema:
|
|
7373
7377
|
$ref: '#/components/schemas/CreateMessageResponse'
|
|
7374
7378
|
example:
|
|
7375
|
-
messageId:
|
|
7379
|
+
messageId: ''
|
|
7376
7380
|
'400':
|
|
7377
7381
|
description: Invalid request payload or plugin dispatch request error
|
|
7378
7382
|
content:
|
|
@@ -20851,7 +20855,10 @@ components:
|
|
|
20851
20855
|
properties:
|
|
20852
20856
|
messageId:
|
|
20853
20857
|
type: string
|
|
20854
|
-
description:
|
|
20858
|
+
description: >-
|
|
20859
|
+
Identifies the unary response message when one is returned. An accepted asynchronous
|
|
20860
|
+
request can return an empty string with no message. This field does not provide a
|
|
20861
|
+
durable dispatch receipt, retry deduplication or proof of observed device state.
|
|
20855
20862
|
message:
|
|
20856
20863
|
$ref: '#/components/schemas/Message'
|
|
20857
20864
|
description: >-
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"properties": {
|
|
8
8
|
"messageId": {
|
|
9
9
|
"type": "string",
|
|
10
|
-
"description": "
|
|
10
|
+
"description": "Identifies the unary response message when one is returned. An accepted asynchronous request can return an empty string with no message. This field does not provide a durable dispatch receipt, retry deduplication or proof of observed device state."
|
|
11
11
|
},
|
|
12
12
|
"message": {
|
|
13
13
|
"$ref": "/docs/references/schemas/Message.json",
|
package/package.json
CHANGED