specs/management-api.feature
Rama main · réplica de lectura del repositorio original.
Feature: Unified Realmroot resource API
As an Agent operator
I want Restish to discover every operation from one Realmroot API contract
So that every resource has one canonical URI and explicit authorization scopes
Background:
Given a first admin exists
@entrypoint:agent-protocol @journey:public-service-discovery @proof:unit
Scenario: Agents discover the service from its public origin
When an anonymous client requests public service discovery
Then root OAuth and OpenAPI aliases redirect to their existing canonical API endpoints
And the API catalog links to the API contract, documentation, health, and protected resource metadata
And the static homepage advertises discovery links and always serves HTML
And robots.txt references a sitemap containing only public service pages
And static discovery documents and fixed redirects are served directly by Workers Assets
And the homepage never enters the Worker for content negotiation
And public discovery supports HEAD without requiring database or authentication initialization
@entrypoint:restish @journey:management-resource-identifiers @proof:unit
Scenario: Newly created resources use standard opaque identifiers
When Realmroot creates a persistent resource or event record
Then its identifier is a UUID version 7 without a resource-type prefix
And existing prefixed identifiers remain valid resource references
And credentials, tokens, protocol nonce values, and request trace identifiers retain their dedicated formats
@entrypoint:restish @journey:management-openapi-discovery @proof:unit
Scenario: The unified API contract is discoverable
When an API client requests service discovery
Then /api/openapi.json returns the OpenAPI 3.1 contract
And /api/docs renders interactive API documentation from that contract
And every operation is grouped under a declared domain tag
And every Realm resource operation is grouped under the Platform tag
And every Realmroot-owned API operation except Account Center, hosted OAuth UI, health, hosted configuration, first-run onboarding, and Better Auth protocol operations appears in the contract
And API responses advertise that contract with Restish-compatible Link headers
And the default hosted Restish profile targets https://id.realmroot.dev/api
And Restish v2 exposes the current Agent and resource operations from the same contract
And resources are not grouped under a management path
And every protected operation declares its exact authorization scope through an OpenAPI security requirement
And the contract declares agentAssertion, oauth2, and session-cookie security schemes
And oauth2 identifies all Resource API credentials regardless of whether the principal is an Agent or Application
And Agent Resource API operations use oauth2 while AgentAuth assertions remain limited to enrollment and token exchange
And no API-key, capability extension, or plugin provider name appears as a public security scheme
And Restish can validate structured authorization detail request bodies without the root OpenAPI document
@entrypoint:agent-protocol @journey:agent-skills-discovery @proof:unit
Scenario: Realmroot publishes instructions for Agents using its Toolbox
When an Agent client requests /.well-known/agent-skills/index.json
Then Realmroot returns the Agent Skills Discovery version 0.2.0 index
And the index advertises every Realmroot-owned Skill as an archive
And each archive contains the Skill instructions and supporting files
And every advertised SHA-256 digest matches its archive bytes
And the Realmroot Skill directs Agents to install task-relevant Skills advertised by a selected Resource Server
@entrypoint:restish @journey:management-collection-envelope @proof:unit
Scenario: Collection responses use one stable envelope
When an authorized caller lists any growing resource collection
Then the response exposes the resources through an items field
And pagination metadata is exposed through a pagination field
And every collection uses one-based page and page size pagination with exact item and page totals
And applicable first, previous, next, and last navigation is exposed through an RFC 8288 Link header
And no collection uses a resource-specific plural field as its envelope
@entrypoint:restish @journey:management-realmroot-resource-server-origin @proof:unit
Scenario: The built-in Realmroot Resource Server follows deployment configuration
Given the deployment canonical origin or Realmroot scope catalog changed since the Resource Server was persisted
When an authorized caller lists Resource Servers
Then the built-in Realmroot Resource Server uses the current deployment API URL
And its persisted scope registry matches the current system-managed Realmroot catalog
When an administrator refreshes that scope registry
Then Realmroot returns the same current catalog without fetching its own public endpoint
And its system identifier, platform owner, and native authorization model remain immutable
@entrypoint:restish @journey:management-restish-command-surface @proof:unit
Scenario: Restish keeps routine resource operations on its generic HTTP surface
Given Restish is connected to the unified Realmroot API
When Restish builds its command metadata from the OpenAPI contract
Then routine single-request operations remain discoverable from the published OpenAPI paths
And those operations use Restish get, post, put, patch, delete, or edit instead of generated commands
And Resource Server and Resource discovery use Restish's generic get command
And only Agent enrollment, Agent identity, and access approval retain generated workflow commands
And those workflows are exposed as enroll, whoami, and access
And account connection and expansion occur inside the access workflow
And polling and short-lived credential issuance remain hidden behind the plugin's generic response protocols
@entrypoint:restish @journey:management-restish-agent-auth @proof:unit
Scenario: Restish transparently authenticates as an Agent
Given Restish is connected to the unified Realmroot API
When a new Agent invokes the generated Agent enrollment operation
Then the Restish authentication adapter starts AgentAuth enrollment without a login command
And the adapter uses the endpoints and issuer published by AgentAuth discovery
And Realmroot does not provision a shared CLI OAuth application
And the enrollment operation waits for one controller approval
And the adapter exchanges the Agent identity assertion only at the OAuth token endpoint
And every later command-line request uses a short-lived audience-restricted DPoP access token
And the adapter caches validated Agent discovery metadata per Realmroot origin for a bounded lifetime
And each token request contains only the scopes required by the selected operation and its declared polling workflow
And every protected Realmroot operation authenticates the same Agent issuer and subject from that token
And a configured credential override can deliberately select a non-Agent oauth2 principal
And the approving user's identity is never used as the command-line principal
And invoking whoami before enrollment fails without creating identity state
@entrypoint:agent-protocol @journey:management-standard-agent-oauth @proof:unit
Scenario: Realmroot authenticates Agent API requests through standard OAuth security
Given an enrolled Agent has a locally protected signing key
When the plugin requests Realmroot API access from the OAuth token endpoint
Then it uses the RFC 7523 JWT bearer grant and an RFC 9449 DPoP proof
And Realmroot issues a short-lived DPoP-bound access token restricted to the Realmroot API audience
And the issued scopes are a subset of the Agent's automatic or controller-approved authority
And the Agent identity assertion is never accepted directly by a Realmroot Resource API operation
And Realmroot publishes its authorization server and DPoP requirements through standard OAuth metadata
@entrypoint:restish @journey:management-native-device-approval @proof:unit
Scenario: Native clients request OAuth device authorization codes when explicitly configured
Given a public native application is configured with the OAuth device-code grant
When a native client requests a device authorization code with JSON or standard form encoding for openid profile email offline_access scopes
Then Realmroot returns a device code, user code, verification URI, expiry, and polling interval
And the native client can poll the OAuth token endpoint for OIDC-compatible tokens after browser approval
And requested resources are bound when the device code is issued
And an OAuth device code cannot be redeemed for a first-party browser session
And confidential, disabled, or non-native clients cannot use device authorization
@entrypoint:restish @journey:management-restish-oauth-crud @proof:integration
Scenario: An authorized Agent manages applications through the unified API
Given the Agent has approved applications:read and applications:write scopes
When I create, update, list, and delete an application with Restish
Then the unified API applies each application change
And Application authorizations form one Realm inventory whose application and status filters are optional
@entrypoint:restish @journey:management-machine-application-crud @proof:unit
Scenario: A Machine Application manages resources within its owner Organization
Given a Machine Application has a direct Permission and matching configured Resource scope
When it presents a client-credentials Bearer access token to the unified API
Then Realmroot authenticates the Application principal
And the required OAuth scope is enforced for each operation
And the Application can operate only within its persisted owner Organization
And it cannot use its Organization scope to access another Organization or the platform Organization
@entrypoint:restish @journey:management-restish-user-crud @proof:unit
Scenario: Platform administration follows the built-in Organization authority
Given the bootstrap administrator is an Owner of the built-in platform Organization
And no runtime authorization rule treats the administrator role as a permission bypass
When a platform Organization member or Agent presents platform-bound users:read and users:write scopes
Then Realm-wide user management follows those Organization scopes
Given an Agent has direct users:read and users:write scopes bound to a User or Organization tenant
When it attempts Realm-wide user management with Restish
Then collection reads are tenant-filtered
And Realm-wide user mutations are rejected
@entrypoint:restish @journey:management-restish-organization-crud @proof:integration
Scenario: User-owned Agents manage only explicitly delegated Organization authority
Given an Agent has organizations:read and organizations:write scopes bound to one Organization
When it lists, reads, or updates organizations with Restish
Then the unified API exposes only that Organization
And Organization creation requires platform Organization authority
And Organization deletion requires authority over the owning Organization
And Organization, member, and invitation creation return their canonical locations
@entrypoint:restish @journey:management-restish-role-crud @proof:unit
Scenario: An authorized Organization user manages dynamic Roles through the unified API
Given the Organization membership maps Better Auth Roles to roles:read and roles:write scopes
When I create, update, list, and delete a Role with its Resource Server scope references
Then the unified API applies each role change
And each role scope must exist in its business resource server protected-resource metadata
And Role creation returns its canonical location
And member Role replacement uses the Organization member Roles child resource
And Agents and workloads cannot receive Organization Roles
@entrypoint:restish @journey:management-restish-api-resource-crud @proof:integration
Scenario: An authorized Agent manages Resource Servers without duplicating business authorization definitions
Given the Agent has approved resource-servers:read and resource-servers:write scopes for the owning Organization
When I create, update, list, and delete an API resource with Restish
Then the unified API applies each API authorization change
And API resource creation returns its canonical location
And no permission catalog, scope catalog, or scope mutation operation exists
And requestable scopes come only from each business resource server's protected-resource metadata
And every external Resource Server is owned by the built-in platform Organization
And an ordinary Organization cannot create, adopt, or update an external Resource Server
And only platform Organization authority can use a platform Connector
@entrypoint:restish @journey:management-api-resource-soft-delete @proof:unit
Scenario: API resources are soft-deleted without losing authorization history
Given the Agent has approved resource-servers:write scope
And an API resource has authorization history
When I delete the API resource with Restish
Then the resource disappears from every Management and discovery interface
And its active authorization records are revoked while history remains
And the deletion and its actor are recorded in the Agent audit log
And concurrent requests cannot create new active authorization for the deleted resource
And its identifier and service URL can be reused by a newly created resource
And concurrent creates for the same identifier or service URL produce one resource and one conflict
And the first deletion returns success while a completed retry returns not found
And no API can restore the resource
@entrypoint:restish @journey:management-restish-webhook-crud @proof:integration
Scenario: An authorized Agent manages webhook endpoints
Given the Agent has approved webhooks:read and webhooks:write scopes
When I create, update, rotate, list, and delete a webhook endpoint with Restish
Then the unified API applies each webhook change
And retrying a delivery request creates a new delivery attempt resource
And every retry requires an idempotency key scoped to the delivery request
And replaying the same key returns the same attempt without delivering twice
@entrypoint:restish @journey:management-canonical-authority-inventory @proof:unit
Scenario: Authority records keep one canonical URI across product surfaces
Given Organization memberships and Agent access records exist for User and Organization tenants
When a Realm operator, Organization developer, or Account Center member reads authority inventory
Then each Organization Role, member Role collection, User Permission, Application Permission, Agent access request, and Agent Permission has one canonical API URI below its owning subject
And filters only narrow the inventory and are never required to establish resource ownership
And the server limits each principal to the records that principal may inspect
And no top-level Entitlement inventory or duplicate Account Center Entitlement URI exists
@entrypoint:restish @journey:management-tenant-owner-enforcement @proof:unit
Scenario: Resource ownership is enforced consistently across the Management API
Given User, Organization, and platform Organization resources exist
When a Session or tenant-bound Agent lists, reads, updates, or deletes those resources
Then required scopes and the persisted resource boundary are checked by the same authorizer
And creating an Organization-owned Application or Resource server requires an explicit owner selector
And an owner selector can only narrow to a tenant the caller controls
And every persisted Organization is exposed and authorized through ordinary Organization membership
And platform-wide operations are authorized through membership and scopes in the built-in platform Organization
And no administrator role, principal type, or Realm authority bypasses that Organization boundary
@entrypoint:restish @journey:management-restish-settings-update @proof:integration
Scenario: An authorized Agent manages tenant settings
Given the Agent has approved settings:read, settings:write, security:read, and security:write scopes
When I update branding, Account Center, sign-in, Realm, Organization creation policy, Developer Console access policy, Email delivery configuration, and security resources with Restish
Then the unified API persists each tenant setting change
And replacing Realm, Organization creation, Developer Console access, or Email delivery state requires the current strong entity tag
@entrypoint:agent-protocol @journey:oauth-resource-initialization @proof:integration
Scenario: Only resource authorization accesses OAuth resource policies
Given many OAuth resources are configured
When a new authentication instance serves discovery or session requests
Then initialization and these requests do not read or write the OAuth resource table
When a token request addresses a configured resource
Then only that target resource is read and created if missing
And existing resource policies are preserved
And unregistered or disabled audiences are rejected
@entrypoint:agent-protocol @journey:jwt-storage-failure @proof:integration
Scenario: Signing key storage failures are not reported as invalid credentials
Given a client presents an OAuth access token
When reading its verification keys fails
Then the protected API returns a server error rather than an authentication failure
And malformed or invalid tokens still return an authentication failure
@entrypoint:product-ui @journey:lazy-openapi-generation @proof:unit
Scenario: Authentication startup does not generate the management contract
When the Worker loads its route definitions
Then the OpenAPI document is not generated
When a caller requests the management contract
Then the unchanged contract is generated once and reused