Files
rustfs/protocol/agent/v1/fixtures/auth/surface-separation.json
T
overtrue 3b5164032a feat(connect): add device identity store and registration proof
A RustFS cluster device needs a durable identity before it can exchange a
one-time registration token for a certificate. This adds the device-side
half of that exchange, which rustfs/connect already verifies.

`connect::identity` builds the canonical registration transcript frozen by
protocol/agent/v1/registration-proof.md, signs it as low-S ES256, and emits
the PKCS#10 certificate request Connect consumes for its SubjectPublicKeyInfo.
`connect::identity_store` seals the P-256 key at mode 0600 and publishes it
through a no-clobber link, so a retry or a concurrent start returns the
original identity rather than minting a second one, and a corrupt or widened
key is refused rather than silently replaced.

The protocol fixture set is copied here byte-identically because
fixture-sets.json names this repository as the consumer copy; the tests
verify it against its own manifests and cross-verify Connect-produced ECDSA
proofs against transcripts rebuilt locally.

Nothing starts a task or touches the S3 data path: an unenrolled deployment
generates no key and holds no identity.
2026-08-19 12:54:50 +08:00

79 lines
2.9 KiB
JSON

{
"protocolVersion": "v1",
"fixtureSet": "auth",
"fixture": "surface-separation",
"description": "The control surface and the agent surface have disjoint credentials. Neither accepts the other's, and neither OpenAPI document declares the other's security scheme.",
"httpVectors": [
{
"name": "agent client certificate presented to the control surface",
"surface": "/api",
"request": {
"method": "GET",
"path": "/api/session",
"headers": {
"Client-Cert": ":MIIBkDCCATagAwIBAgIQZXhhbXBsZQ==:"
},
"browserSession": false
},
"expected": {
"authenticated": false,
"httpStatus": 401,
"status": "UNAUTHENTICATED",
"reason": "UNAUTHENTICATED"
}
},
{
"name": "browser session presented to the agent surface",
"surface": "/agent",
"request": {
"method": "GET",
"path": "/agent/protocolStatus",
"headers": {},
"browserSession": true
},
"expected": {
"deviceIdentityEstablished": false,
"httpStatus": 200,
"body": {
"protocolVersion": "v1"
},
"identicalToUnauthenticatedRequest": true,
"note": "getProtocolStatus is the pre-registration operation and is public on purpose. An authenticated browser session neither changes its answer nor grants anything on the agent surface."
}
}
],
"documentVectors": [
{
"document": "openapi/agent.json",
"securityScheme": "agentMutualTls",
"schemeType": "mutualTLS",
"forbiddenSecuritySchemes": ["sessionCookie"],
"defaultSecurity": ["agentMutualTls"],
"publicOperations": ["getProtocolStatus"]
},
{
"document": "openapi/control.json",
"securityScheme": "sessionCookie",
"schemeType": "apiKey",
"forbiddenSecuritySchemes": ["agentMutualTls"],
"defaultSecurity": [],
"publicOperations": null
}
],
"routeGuards": {
"surfacePrefix": "agent/",
"description": "No agent route may be protected by a session authentication guard. A device is identified by its certificate or not at all.",
"forbiddenMiddlewarePrefixes": ["auth:", "auth.session"],
"forbiddenMiddleware": ["auth"],
"forbiddenMiddlewareClasses": [
"Illuminate\\Auth\\Middleware\\Authenticate",
"Illuminate\\Auth\\Middleware\\AuthenticateSession"
],
"knownGap": {
"middleware": "Laravel\\Sanctum\\Http\\Middleware\\EnsureFrontendRequestsAreStateful",
"description": "The agent routes still share the api middleware group with the control surface, so Sanctum's stateful frontend middleware runs on them. It establishes no device identity and no agent route uses an authentication guard, but the agent surface should get its own middleware group when the first authenticated agent operation lands.",
"owner": "the issue that adds the first authenticated agent operation"
}
}
}