Add anonymous OOBE gateway grants
CI / verify (push) Has been cancelled
CI / publish (push) Has been cancelled

Provide App Attest-bound, one-time onboarding AI access without creating accounts, with durable replay protection and production deployment safeguards.
This commit is contained in:
Rocky
2026-08-21 22:55:46 +08:00
parent edd0d9feca
commit 0d236f57fb
32 changed files with 2202 additions and 61 deletions
+86 -5
View File
@@ -381,6 +381,43 @@ paths:
responses:
"200": { description: Assertion counter advanced }
default: { $ref: "#/components/responses/Error" }
/v1/oobe/grants:
post:
security: []
summary: Create a short-lived anonymous OOBE gateway grant
description: |
Verifies an App Attest assertion bound to the installation and returns
credentials limited to the four one-time onboarding AI features.
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/CreateOobeGrantRequest" }
responses:
"201":
description: OOBE gateway credentials
content:
application/json:
schema: { $ref: "#/components/schemas/OobeGrantTokens" }
default: { $ref: "#/components/responses/GatewayError" }
/v1/oobe/grants/refresh:
post:
security: []
summary: Rotate an anonymous OOBE refresh token
parameters:
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/RefreshOobeGrantRequest" }
responses:
"200":
description: Rotated OOBE gateway credentials
content:
application/json:
schema: { $ref: "#/components/schemas/OobeGrantTokens" }
default: { $ref: "#/components/responses/GatewayError" }
/v1/gateway/catalog:
get:
summary: Return configured managed-provider capabilities
@@ -441,9 +478,10 @@ paths:
and output-budget policy from `capability` plus optional `taskKind`. It
never infers task type from `input` or `context`, and clients cannot
supply provider parameters. Search and tools are currently disabled.
An authenticated `oobe` purpose is accepted only for dictation polish.
The first successful request per account is complimentary; later attempts
fail without falling through to paid billing.
For account grants, `oobe` is accepted only for dictation polish and the
first successful request per account is complimentary. Anonymous OOBE
grants require a matching `oobeFeature` and allow one successful request
per feature. Later attempts fail without falling through to paid billing.
parameters:
- $ref: "#/components/parameters/RequestId"
- name: capability
@@ -2629,8 +2667,51 @@ components:
type: ["string", "null"]
enum: [oobe, null]
description: |
Optional server-audited billing purpose. `oobe` is valid only with
`polish` and `dictation_polish`, and is complimentary once per account.
Optional server-audited billing purpose. Account grants accept `oobe`
only for complimentary dictation polish. Anonymous OOBE grants require
`oobe` together with an `oobeFeature`.
oobeFeature:
type: ["string", "null"]
enum: [voice_input, clipboard_translate, clipboard_reply, ask_ai, null]
description: |
Required for anonymous OOBE grants. The server validates that the
feature matches the requested capability and task kind, and allows
each feature to succeed only once per installation-bound subject.
CreateOobeGrantRequest:
type: object
additionalProperties: false
required: [challengeId, challenge, keyId, installationId, assertion]
properties:
challengeId: { type: string, format: uuid }
challenge: { type: string, description: Base64URL challenge returned by the integrity API }
keyId: { type: string, minLength: 1, maxLength: 256 }
installationId: { type: string, format: uuid }
assertion: { type: string, contentEncoding: base64 }
RefreshOobeGrantRequest:
type: object
additionalProperties: false
required: [refreshToken]
properties:
refreshToken: { type: string, minLength: 32, maxLength: 512 }
OobeGrantTokens:
type: object
additionalProperties: false
required:
[grantId, scopes, features, accessToken, accessExpiresAt, refreshToken, refreshExpiresAt]
properties:
grantId: { type: string, format: uuid }
scopes:
type: array
uniqueItems: true
items: { type: string, enum: [polish, ai] }
features:
type: array
uniqueItems: true
items: { type: string, enum: [voice_input, clipboard_translate, clipboard_reply, ask_ai] }
accessToken: { type: string }
accessExpiresAt: { type: string, format: date-time }
refreshToken: { type: string }
refreshExpiresAt: { type: string, format: date-time }
CreateGatewayGrantRequest:
type: object
additionalProperties: false