Specify fenced activation and source credential contracts

Define bounded reservation recovery, exact activation replay fencing, immutable source identities, and deployment-owned secret and target policy boundaries. Align the OpenAPI contract and add a split-store activation safety refinement.
This commit is contained in:
ms2k 2026-07-14 18:12:28 +09:00
commit 033cfeaac6

View file

@ -257,6 +257,113 @@ paths:
$ref: '#/components/responses/NotFound'
default:
$ref: '#/components/responses/Error'
/v1/source-credentials:
post:
operationId: createSourceCredential
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SourceCredentialCreate'
responses:
'201':
description: Credential metadata and first immutable version created, or the original response replayed.
headers:
Idempotency-Expires-At:
$ref: '#/components/headers/IdempotencyExpiresAt'
content:
application/json:
schema:
$ref: '#/components/schemas/SourceCredential'
'409':
$ref: '#/components/responses/IdempotencyConflict'
default:
$ref: '#/components/responses/Error'
get:
operationId: listSourceCredentials
parameters:
- $ref: '#/components/parameters/Cursor'
- $ref: '#/components/parameters/Limit'
responses:
'200':
description: Authorized credential metadata page without secret material.
content:
application/json:
schema:
$ref: '#/components/schemas/SourceCredentialPage'
default:
$ref: '#/components/responses/Error'
/v1/source-credentials/{credential_id}:
parameters:
- $ref: '#/components/parameters/CredentialId'
get:
operationId: getSourceCredential
responses:
'200':
description: Authorized credential metadata without secret material.
content:
application/json:
schema:
$ref: '#/components/schemas/SourceCredential'
'404':
$ref: '#/components/responses/NotFound'
default:
$ref: '#/components/responses/Error'
/v1/source-credentials/{credential_id}/versions:
parameters:
- $ref: '#/components/parameters/CredentialId'
post:
operationId: rotateSourceCredential
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SourceCredentialRotate'
responses:
'201':
description: New immutable version created and selected for future grants, or the original response replayed.
headers:
Idempotency-Expires-At:
$ref: '#/components/headers/IdempotencyExpiresAt'
content:
application/json:
schema:
$ref: '#/components/schemas/SourceCredential'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/IdempotencyConflict'
default:
$ref: '#/components/responses/Error'
/v1/source-credentials/{credential_id}/revoke:
parameters:
- $ref: '#/components/parameters/CredentialId'
post:
operationId: revokeSourceCredential
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
responses:
'200':
description: Credential revoked for future grants, or the original response replayed.
headers:
Idempotency-Expires-At:
$ref: '#/components/headers/IdempotencyExpiresAt'
content:
application/json:
schema:
$ref: '#/components/schemas/SourceCredential'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/IdempotencyConflict'
default:
$ref: '#/components/responses/Error'
/v1/deletions/{operation_id}:
parameters:
- name: operation_id
@ -308,11 +415,13 @@ paths:
$ref: '#/components/schemas/ClaimRequest'
responses:
'200':
description: Allocated authority or replay of the same claim operation.
description: Reserved and durably authorized attempt, or replay of the same claim operation. The authority is non-executable until activateAttempt succeeds.
content:
application/json:
schema:
$ref: '#/components/schemas/ClaimResponse'
'409':
$ref: '#/components/responses/IdempotencyConflict'
default:
$ref: '#/components/responses/Error'
/internal/v1/attempts/{attempt_id}/renew:
@ -335,6 +444,70 @@ paths:
$ref: '#/components/responses/PreconditionFailed'
default:
$ref: '#/components/responses/Error'
/internal/v1/attempts/{attempt_id}/activate:
parameters:
- $ref: '#/components/parameters/AttemptId'
post:
operationId: activateAttempt
security:
- serviceAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AttemptActivateRequest'
responses:
'200':
description: Exact authority activated, or a prior successful activation replayed while its lease and durable executing tuple still agree.
content:
application/json:
schema:
$ref: '#/components/schemas/ActivationResponse'
'409':
$ref: '#/components/responses/PreconditionFailed'
default:
$ref: '#/components/responses/Error'
/internal/v1/attempts/{attempt_id}/resolved-source:
parameters:
- $ref: '#/components/parameters/AttemptId'
post:
operationId: pinAttemptResolvedSource
security:
- serviceAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AttemptResolvedSourceRequest'
responses:
'204':
description: Resolved source pinned once, or the exact operation replayed.
'409':
$ref: '#/components/responses/PreconditionFailed'
default:
$ref: '#/components/responses/Error'
/internal/v1/attempts/{attempt_id}/checkpoint:
parameters:
- $ref: '#/components/parameters/AttemptId'
post:
operationId: persistAttemptCheckpoint
security:
- serviceAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AttemptCheckpointRequest'
responses:
'204':
description: Validated checkpoint persisted, or the exact operation replayed.
'409':
$ref: '#/components/responses/PreconditionFailed'
default:
$ref: '#/components/responses/Error'
/internal/v1/attempts/{attempt_id}/progress:
parameters:
- $ref: '#/components/parameters/AttemptId'
@ -422,6 +595,12 @@ components:
required: true
schema:
$ref: '#/components/schemas/OpaqueId'
CredentialId:
name: credential_id
in: path
required: true
schema:
$ref: '#/components/schemas/CredentialId'
headers:
IdempotencyExpiresAt:
description: Earliest time at which the server may forget this key.
@ -461,6 +640,14 @@ components:
description: Internal stable principal identifier mapped to one exact OIDC issuer and subject pair.
allOf:
- $ref: '#/components/schemas/OpaqueId'
CredentialId:
description: Owner-authorized source credential registry identifier.
allOf:
- $ref: '#/components/schemas/OpaqueId'
CredentialVersionId:
description: Immutable application credential-version identifier; backend secret-store coordinates are never exposed.
allOf:
- $ref: '#/components/schemas/OpaqueId'
Target:
type: object
additionalProperties: false
@ -506,10 +693,15 @@ components:
minLength: 1
maxLength: 1024
description: Exact attempt-scoped internal prefix selected by the service.
HttpSource:
ResultPath:
type: string
minLength: 1
maxLength: 4096
description: NFC-normalized relative POSIX path validated under architecture.md.
HttpSourceSubmission:
type: object
additionalProperties: false
required: [kind, url]
required: [kind, url, result_path]
properties:
kind:
const: http
@ -517,13 +709,16 @@ components:
type: string
format: uri
maxLength: 8192
credential_ref:
type: string
maxLength: 512
ObjectStoreSource:
writeOnly: true
description: Sensitive execution input. Public projections expose only display_url.
result_path:
$ref: '#/components/schemas/ResultPath'
credential_id:
$ref: '#/components/schemas/CredentialId'
ObjectStoreSourceSubmission:
type: object
additionalProperties: false
required: [kind, store, bucket, key]
required: [kind, store, bucket, key, result_path]
properties:
kind:
const: object_store
@ -542,10 +737,33 @@ components:
version_id:
type: string
maxLength: 1024
credential_ref:
type: string
maxLength: 512
HuggingFaceSource:
result_path:
$ref: '#/components/schemas/ResultPath'
credential_id:
$ref: '#/components/schemas/CredentialId'
HuggingFaceSelection:
type: object
additionalProperties: false
minProperties: 1
anyOf:
- required: [paths]
- required: [prefixes]
properties:
paths:
type: array
minItems: 1
maxItems: 1024
uniqueItems: true
items:
$ref: '#/components/schemas/ResultPath'
prefixes:
type: array
minItems: 1
maxItems: 1024
uniqueItems: true
items:
$ref: '#/components/schemas/ResultPath'
HuggingFaceSourceSubmission:
type: object
additionalProperties: false
required: [kind, repository_type, repository_id, revision]
@ -562,35 +780,102 @@ components:
type: string
minLength: 1
maxLength: 256
include:
type: array
maxItems: 1024
uniqueItems: true
items:
type: string
minLength: 1
maxLength: 1024
credential_ref:
type: string
maxLength: 512
Source:
selection:
$ref: '#/components/schemas/HuggingFaceSelection'
credential_id:
$ref: '#/components/schemas/CredentialId'
SourceSubmission:
oneOf:
- $ref: '#/components/schemas/HttpSource'
- $ref: '#/components/schemas/ObjectStoreSource'
- $ref: '#/components/schemas/HuggingFaceSource'
- $ref: '#/components/schemas/HttpSourceSubmission'
- $ref: '#/components/schemas/ObjectStoreSourceSubmission'
- $ref: '#/components/schemas/HuggingFaceSourceSubmission'
discriminator:
propertyName: kind
mapping:
http: '#/components/schemas/HttpSource'
object_store: '#/components/schemas/ObjectStoreSource'
hugging_face: '#/components/schemas/HuggingFaceSource'
http: '#/components/schemas/HttpSourceSubmission'
object_store: '#/components/schemas/ObjectStoreSourceSubmission'
hugging_face: '#/components/schemas/HuggingFaceSourceSubmission'
HttpSourceProjection:
type: object
additionalProperties: false
required: [kind, display_url, result_path]
properties:
kind:
const: http
display_url:
type: string
format: uri
maxLength: 8192
description: Sanitized URL without userinfo, sensitive query values, or fragments.
result_path:
$ref: '#/components/schemas/ResultPath'
credential_id:
$ref: '#/components/schemas/CredentialId'
ObjectStoreSourceProjection:
type: object
additionalProperties: false
required: [kind, store, bucket, key, result_path]
properties:
kind:
const: object_store
store:
type: string
minLength: 1
maxLength: 128
bucket:
type: string
minLength: 3
maxLength: 63
key:
type: string
minLength: 1
maxLength: 4096
version_id:
type: string
maxLength: 1024
result_path:
$ref: '#/components/schemas/ResultPath'
credential_id:
$ref: '#/components/schemas/CredentialId'
HuggingFaceSourceProjection:
type: object
additionalProperties: false
required: [kind, repository_type, repository_id, revision]
properties:
kind:
const: hugging_face
repository_type:
type: string
enum: [model, dataset, space]
repository_id:
type: string
pattern: '^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$'
revision:
type: string
minLength: 1
maxLength: 256
selection:
$ref: '#/components/schemas/HuggingFaceSelection'
credential_id:
$ref: '#/components/schemas/CredentialId'
SourceProjection:
oneOf:
- $ref: '#/components/schemas/HttpSourceProjection'
- $ref: '#/components/schemas/ObjectStoreSourceProjection'
- $ref: '#/components/schemas/HuggingFaceSourceProjection'
discriminator:
propertyName: kind
mapping:
http: '#/components/schemas/HttpSourceProjection'
object_store: '#/components/schemas/ObjectStoreSourceProjection'
hugging_face: '#/components/schemas/HuggingFaceSourceProjection'
JobSubmission:
type: object
additionalProperties: false
required: [source]
properties:
source:
$ref: '#/components/schemas/Source'
$ref: '#/components/schemas/SourceSubmission'
target:
description: Fields omitted here are taken from the configured default target before authorization.
$ref: '#/components/schemas/TargetOverride'
@ -598,6 +883,97 @@ components:
type: string
minLength: 1
maxLength: 200
SourceCredentialKind:
type: string
enum: [basic_auth, bearer_token, http_headers, object_store, hugging_face]
CredentialMaterial:
type: object
minProperties: 1
maxProperties: 64
additionalProperties:
type: string
minLength: 1
maxLength: 16384
writeOnly: true
description: Write-only adapter credential fields. Values are never returned or logged.
SourceCredentialCreate:
type: object
additionalProperties: false
required: [label, kind, material]
properties:
label:
type: string
minLength: 1
maxLength: 200
kind:
$ref: '#/components/schemas/SourceCredentialKind'
material:
$ref: '#/components/schemas/CredentialMaterial'
SourceCredentialRotate:
type: object
additionalProperties: false
required: [material]
properties:
material:
$ref: '#/components/schemas/CredentialMaterial'
SourceCredentialVersion:
type: object
additionalProperties: false
required: [id, created_at]
properties:
id:
$ref: '#/components/schemas/CredentialVersionId'
created_at:
type: string
format: date-time
CredentialGrantVersion:
type: object
additionalProperties: false
required: [credential_id, version_id]
properties:
credential_id:
$ref: '#/components/schemas/CredentialId'
version_id:
$ref: '#/components/schemas/CredentialVersionId'
SourceCredential:
type: object
additionalProperties: false
required: [id, owner, label, kind, status, active_version, created_at, updated_at]
properties:
id:
$ref: '#/components/schemas/CredentialId'
owner:
$ref: '#/components/schemas/PrincipalId'
label:
type: string
minLength: 1
maxLength: 200
kind:
$ref: '#/components/schemas/SourceCredentialKind'
status:
type: string
enum: [active, revoked]
active_version:
oneOf:
- $ref: '#/components/schemas/SourceCredentialVersion'
- type: 'null'
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
SourceCredentialPage:
type: object
additionalProperties: false
required: [items, next_cursor]
properties:
items:
type: array
items:
$ref: '#/components/schemas/SourceCredential'
next_cursor:
type: [string, 'null']
JobPhase:
type: string
enum: [Queued, Running, RetryWait, Cancelling, Succeeded, Failed, Cancelled]
@ -802,7 +1178,7 @@ components:
lifecycle:
$ref: '#/components/schemas/JobLifecycle'
source:
$ref: '#/components/schemas/Source'
$ref: '#/components/schemas/SourceProjection'
target:
$ref: '#/components/schemas/Target'
authoritative_attempt:
@ -978,7 +1354,7 @@ components:
ExecutionEnvelope:
type: object
additionalProperties: false
required: [id, job_id, attempt_id, fence, worker_incarnation, source, resolved_source_ref, target, staging_target, execution_grant_ref, checkpoint_ref]
required: [id, job_id, attempt_id, fence, worker_incarnation, source, sensitive_source_ref, credential_version, resolved_source_ref, target, staging_target, execution_grant_ref, checkpoint_ref]
properties:
id:
$ref: '#/components/schemas/OpaqueId'
@ -992,7 +1368,14 @@ components:
worker_incarnation:
$ref: '#/components/schemas/OpaqueId'
source:
$ref: '#/components/schemas/Source'
$ref: '#/components/schemas/SourceProjection'
sensitive_source_ref:
description: Opaque grant-bound reference to the complete accepted source input; credential_version identifies any pinned credential separately.
$ref: '#/components/schemas/OpaqueId'
credential_version:
oneOf:
- $ref: '#/components/schemas/CredentialGrantVersion'
- type: 'null'
resolved_source_ref:
oneOf:
- $ref: '#/components/schemas/OpaqueId'
@ -1016,6 +1399,15 @@ components:
$ref: '#/components/schemas/AttemptAuthority'
execution:
$ref: '#/components/schemas/ExecutionEnvelope'
ActivationResponse:
type: object
additionalProperties: false
required: [authority, execution]
properties:
authority:
$ref: '#/components/schemas/AttemptAuthority'
execution:
$ref: '#/components/schemas/ExecutionEnvelope'
AttemptMutation:
type: object
required: [operation_id, fence, worker_incarnation]
@ -1031,6 +1423,28 @@ components:
unevaluatedProperties: false
allOf:
- $ref: '#/components/schemas/AttemptMutation'
AttemptActivateRequest:
unevaluatedProperties: false
allOf:
- $ref: '#/components/schemas/AttemptMutation'
AttemptResolvedSourceRequest:
unevaluatedProperties: false
allOf:
- $ref: '#/components/schemas/AttemptMutation'
- type: object
required: [resolved_source_ref]
properties:
resolved_source_ref:
$ref: '#/components/schemas/OpaqueId'
AttemptCheckpointRequest:
unevaluatedProperties: false
allOf:
- $ref: '#/components/schemas/AttemptMutation'
- type: object
required: [checkpoint_ref]
properties:
checkpoint_ref:
$ref: '#/components/schemas/OpaqueId'
AttemptProgressRequest:
unevaluatedProperties: false
allOf: