Authority Effective Write Contract

Contract ID: CONTRACT-AUTHORITY-EFFECTIVE-WRITE-008 Version: 1.0 Status: Published Last Updated: 2025-12-05

Implementation note (reconciled against source). Although the routes are namespaced under /api/v1/authority/..., the endpoints, services, and data models for this contract are implemented in the Policy Engine (src/Policy/StellaOps.Policy.Engine + src/Policy/StellaOps.Policy.RiskProfile), not in the Authority service. The Authority OpenAPI spec only declares the effective:write scope; it does not host these endpoints. Storage is an in-memory store (ConcurrentDictionary inside EffectivePolicyService) — there is currently no PostgreSQL persistence or migration backing this contract. See Implementation References below.

Overview

This contract defines the effective:write scope and the associated APIs for managing effective policies and authority scope attachments in Stella Ops. An effective policy binds a policy pack revision to a subject pattern with a priority rank, controlling which policy governs releases for matching subjects. The APIs are served by the Policy Engine under the /api/v1/authority/... route prefix.

Audience: integrators and operators wiring policy attachment into release control, and service authors that consume the effective:write / policy:read scopes.

Implementation References

Not to be confused with the Risk Profile scope attachments surface (/api/risk/scopes/..., also in ScopeAttachmentEndpoints.cs), which binds risk profiles to organizational scopes (organization/project/environment/component) and is gated by policy:edit / policy:read. That is a separate model (ScopeAttachment) and is not part of this contract. This contract concerns the AuthorityScopeAttachment model under /api/v1/authority/scope-attachments.

Scope Definition

effective:write

The canonical purpose of effective:write (StellaOpsScopes.EffectiveWrite = "effective:write") is: “Scope granted to the Policy Engine service identity for writing effective findings.” The Authority OpenAPI spec describes it as “Write effective findings (Policy Engine service identity only).”

Within this contract the same scope additionally gates the write operations on effective policies and authority scope attachments:

Backward-compatibility fallback: the write endpoints first check for effective:write; if absent they fall back to policy:edit (StellaOpsScopes.PolicyEdit) during the migration period (see RequireEffectiveWriteScope; TODO tracked under POLICY-AOC-19-002).

Read operations require policy:read(StellaOpsScopes.PolicyRead), not effective:write. This applies to get-by-id, list, get-scope-attachments, and resolve.

Tenant context is mandatory. Every endpoint group is registered with RequireTenantContext(); the tenant is taken from the authenticated tenant context / claims, not from a query-string parameter (POL-TEN-03).

Data Models

EffectivePolicy

Source: EffectivePolicy record in ScopeAttachmentModels.cs. JSON property names are snake_case (explicit [JsonPropertyName] attributes).

{
  "effective_policy_id": "eff-0a1b2c3d4e5f6071",
  "tenant_id": "default",
  "policy_id": "security-policy-v1",
  "policy_version": "1.0.0",
  "subject_pattern": "pkg:npm/*",
  "priority": 100,
  "enabled": true,
  "expires_at": "2025-12-31T23:59:59Z",
  "scopes": ["scan:read", "scan:write"],
  "created_at": "2025-12-05T10:00:00Z",
  "created_by": "admin@example.com",
  "updated_at": "2025-12-05T10:00:00Z"
}
FieldTypeRequiredDescription
effective_policy_idstringAutoGenerated as eff- + first 16 hex chars of SHA256(tenant_id|policy_id|subject_pattern|timestamp)
tenant_idstringYesTenant scope
policy_idstringYesReferenced policy pack
policy_versionstring | nullNoSpecific version (null if omitted)
subject_patternstringYesSubject matching pattern (validated, see below)
priorityintegerYesPriority rank (higher = wins)
enabledbooleanNoWhether policy is active (default: true)
expires_atdatetime | nullNoOptional expiration (RFC 3339 / ISO 8601)
scopesarray<string> | nullNoAttached authorization scopes carried on the policy
created_atdatetimeAutoCreation timestamp
created_bystring | nullAutoActor id resolved (in order) from the NameIdentifier claim, then upn, then sub; falls back to the X-StellaOps-Actor header (see ResolveActorId in EffectivePolicyEndpoints.cs)
updated_atdatetimeAutoLast-update timestamp

AuthorityScopeAttachment

Source: AuthorityScopeAttachment record in ScopeAttachmentModels.cs.

{
  "attachment_id": "att-9f8e7d6c5b4a3021",
  "effective_policy_id": "eff-0a1b2c3d4e5f6071",
  "scope": "scan:write",
  "conditions": {
    "environment": "production"
  },
  "created_at": "2025-12-05T10:00:00Z"
}
FieldTypeRequiredDescription
attachment_idstringAutoGenerated as att- + first 16 hex chars of SHA256(effective_policy_id|scope|timestamp)
effective_policy_idstringYesOwning effective policy (must already exist)
scopestringYesAuthorization scope to attach
conditionsobject<string,string> | nullNoFree-form string-to-string conditions map
created_atdatetimeAutoCreation timestamp

Subject Patterns

Subject patterns use glob-style matching and are validated by EffectivePolicyService.IsValidSubjectPattern. A pattern is valid only if it is the universal wildcard *, or starts with pkg: or oci://, and does not contain a double wildcard **.

PatternValidMatches
*YesAll subjects
pkg:*YesAll packages
pkg:npm/*YesAll npm packages
pkg:npm/@org/*Yesnpm packages in the @org scope
pkg:maven/com.example/*YesMaven packages in com.example
oci://registry.example.com/*YesAll images in registry
invalidNoRejected with ERR_AUTH_001
pkg:**NoDouble wildcard rejected with ERR_AUTH_001

Glob matching semantics (MatchesPattern / GlobToRegex): a trailing * matches everything remaining (including /); a non-trailing * matches a single path segment ([^/]*). Matching is case-insensitive.

API Endpoints

All routes are served by the Policy Engine. All endpoints require authentication and a resolved tenant context (RequireTenantContext).

Effective Policies

Create Effective Policy

Requires effective:write (or policy:edit fallback). Audited as policy / create_effective_policy.

POST /api/v1/authority/effective-policies
Content-Type: application/json
Authorization: Bearer <token with effective:write scope>

{
  "tenant_id": "default",
  "policy_id": "security-policy-v1",
  "subject_pattern": "pkg:npm/*",
  "priority": 100,
  "scopes": ["scan:read", "scan:write"]
}

Response: 201 Created
Location: /api/v1/authority/effective-policies/eff-0a1b2c3d4e5f6071
{
  "effectivePolicy": {
    "effective_policy_id": "eff-0a1b2c3d4e5f6071",
    "tenant_id": "default",
    "policy_id": "security-policy-v1",
    "policy_version": null,
    "subject_pattern": "pkg:npm/*",
    "priority": 100,
    "enabled": true,
    "expires_at": null,
    "scopes": ["scan:read", "scan:write"],
    "created_at": "2025-12-05T10:00:00Z",
    "created_by": "admin@example.com",
    "updated_at": "2025-12-05T10:00:00Z"
  }
}

The response is wrapped in an effectivePolicy envelope (EffectivePolicyResponse). Invalid patterns / missing fields return 400 Bad Request with error_code ERR_AUTH_001.

Get Effective Policy

Requires policy:read.

GET /api/v1/authority/effective-policies/{effectivePolicyId}
Authorization: Bearer <token with policy:read scope>

Response: 200 OK
{ "effectivePolicy": { ... } }

Response: 404 Not Found  (error_code: ERR_AUTH_002)

Update Effective Policy

Requires effective:write (or policy:edit fallback). Audited as policy / update_effective_policy. Only priority, enabled, expires_at, and scopes are mutable; subject_pattern, policy_id, and tenant_id cannot be changed.

PUT /api/v1/authority/effective-policies/{effectivePolicyId}
Content-Type: application/json
Authorization: Bearer <token with effective:write scope>

{
  "priority": 150,
  "enabled": false,
  "expires_at": "2025-12-31T23:59:59Z",
  "scopes": ["scan:read"]
}

Response: 200 OK
{ "effectivePolicy": { ... } }

Response: 404 Not Found  (error_code: ERR_AUTH_002)

Delete Effective Policy

Requires effective:write (or policy:edit fallback). Audited as policy / delete_effective_policy. Deleting a policy also removes its associated scope attachments.

DELETE /api/v1/authority/effective-policies/{effectivePolicyId}
Authorization: Bearer <token with effective:write scope>

Response: 204 No Content
Response: 404 Not Found  (error_code: ERR_AUTH_002)

List Effective Policies

Requires policy:read. The tenant is taken from the tenant context, not a query parameter. Results are ordered by priority descending, then updated_at descending.

GET /api/v1/authority/effective-policies?policyId=security-policy-v1&enabledOnly=true&includeExpired=false&limit=100
Authorization: Bearer <token with policy:read scope>

Response: 200 OK
{
  "items": [
    {
      "effective_policy_id": "eff-0a1b2c3d4e5f6071",
      "tenant_id": "default",
      "policy_id": "security-policy-v1",
      "policy_version": null,
      "subject_pattern": "pkg:npm/*",
      "priority": 100,
      "enabled": true,
      "expires_at": null,
      "scopes": ["scan:read", "scan:write"],
      "created_at": "2025-12-05T10:00:00Z",
      "created_by": "admin@example.com",
      "updated_at": "2025-12-05T10:00:00Z"
    }
  ],
  "total": 1
}

Query parameters:

ParameterTypeDefaultDescription
policyIdstring(none)Filter by referenced policy id
enabledOnlybooleanfalseOnly return enabled policies
includeExpiredbooleanfalseInclude policies past expires_at
limitinteger100Max items (values <= 0 default to 100)

Scope Attachments

Attach Scope

Requires effective:write (or policy:edit fallback). Audited as policy / grant_scope.

POST /api/v1/authority/scope-attachments
Content-Type: application/json
Authorization: Bearer <token with effective:write scope>

{
  "effective_policy_id": "eff-0a1b2c3d4e5f6071",
  "scope": "scan:write",
  "conditions": {
    "environment": "production"
  }
}

Response: 201 Created
Location: /api/v1/authority/scope-attachments/att-9f8e7d6c5b4a3021
{
  "attachment": {
    "attachment_id": "att-9f8e7d6c5b4a3021",
    "effective_policy_id": "eff-0a1b2c3d4e5f6071",
    "scope": "scan:write",
    "conditions": { "environment": "production" },
    "created_at": "2025-12-05T10:00:00Z"
  }
}

The response is wrapped in an attachment envelope (AuthorityScopeAttachmentResponse). Attaching to a non-existent policy returns 400 Bad Request with error_code ERR_AUTH_002; other validation failures return ERR_AUTH_004.

Detach Scope

Requires effective:write (or policy:edit fallback). Audited as policy / revoke_scope.

DELETE /api/v1/authority/scope-attachments/{attachmentId}
Authorization: Bearer <token with effective:write scope>

Response: 204 No Content
Response: 404 Not Found

List Scope Attachments for a Policy

Requires policy:read.

GET /api/v1/authority/scope-attachments/policy/{effectivePolicyId}
Authorization: Bearer <token with policy:read scope>

Response: 200 OK
{
  "attachments": [
    {
      "attachment_id": "att-9f8e7d6c5b4a3021",
      "effective_policy_id": "eff-0a1b2c3d4e5f6071",
      "scope": "scan:write",
      "conditions": { "environment": "production" },
      "created_at": "2025-12-05T10:00:00Z"
    }
  ]
}

Policy Resolution

Resolve Effective Policy for Subject

Requires policy:read. The tenant is taken from the tenant context. A blank subject returns 400 Bad Request.

GET /api/v1/authority/resolve?subject=pkg:npm/lodash@4.17.20
Authorization: Bearer <token with policy:read scope>

Response: 200 OK
{
  "result": {
    "subject": "pkg:npm/lodash@4.17.20",
    "effective_policy": {
      "effective_policy_id": "eff-0a1b2c3d4e5f6071",
      "tenant_id": "default",
      "policy_id": "security-policy-v1",
      "policy_version": null,
      "subject_pattern": "pkg:npm/*",
      "priority": 100,
      "enabled": true,
      "expires_at": null,
      "scopes": ["scan:read", "scan:write"],
      "created_at": "2025-12-05T10:00:00Z",
      "created_by": "admin@example.com",
      "updated_at": "2025-12-05T10:00:00Z"
    },
    "granted_scopes": ["scan:read", "scan:write"],
    "matched_pattern": "pkg:npm/*",
    "resolution_time_ms": 0.42
  }
}

The response is wrapped in a result envelope (EffectivePolicyResolutionResponse). When no policy matches, effective_policy and matched_pattern are null and granted_scopes is an empty array. granted_scopes is the union of the winning policy’s scopes plus any attached scopes.

Priority Resolution

When multiple enabled, non-expired effective policies match a subject within the resolved tenant (EffectivePolicyService.Resolve):

  1. Higher priority value wins.
  2. If equal priority, the more specific pattern wins (GetPatternSpecificity = literalChars * 10 + segmentCount * 5; the universal * scores 0).
  3. If equal specificity, the most recently updated (updated_at) wins.

Example:

Pattern: pkg:npm/*         Priority: 100  → Matches
Pattern: pkg:npm/@org/*    Priority: 100  → Matches (more specific)
Pattern: pkg:*             Priority: 100  → Matches

Winner (for subject pkg:npm/@org/utils@1.0.0): pkg:npm/@org/*
        (most specific among equal-priority matches)

Audit Trail

All effective:write operations are recorded two ways:

  1. Structured log scope emitted by EffectivePolicyAuditor (EffectivePolicyEndpoints calls RecordCreated / RecordUpdated / RecordDeleted / RecordScopeAttached / RecordScopeDetached).
  2. Audit pipeline via the .Audited(AuditModules.Policy, ...) endpoint filter (AuditActions.Policy.CreateEffectivePolicy etc.).

The structured log scope shape (example for a creation event):

{
  "event": "effective_policy.created",
  "timestamp": "2025-12-05T10:00:00.0000000Z",
  "actor": "admin@example.com",
  "effective_policy_id": "eff-0a1b2c3d4e5f6071",
  "tenant_id": "default",
  "policy_id": "security-policy-v1",
  "subject_pattern": "pkg:npm/*",
  "priority": 100,
  "scopes": ["scan:read", "scan:write"]
}

Event types: effective_policy.created, effective_policy.updated, effective_policy.deleted, scope_attachment.created, scope_attachment.deleted. Audit action ids (audit pipeline): create_effective_policy, update_effective_policy, delete_effective_policy, grant_scope, revoke_scope.

Error Codes

These error codes are emitted as the error_code extension on the RFC 7807 ProblemDetails body.

CodeWhere usedMessage
ERR_AUTH_001Create policy — invalid subject patternInvalid subject pattern
ERR_AUTH_002Get/Update/Delete policy not found; attach to non-existent policyPolicy not found
ERR_AUTH_004Attach scope — other validation failureInvalid scope

Not implemented (documented for completeness): ERR_AUTH_003 (Duplicate attachment) and ERR_AUTH_005 (Priority conflict) appear in earlier drafts of this contract but are not produced by the current implementation. Attaching a duplicate scope or creating policies with conflicting priorities does not raise a dedicated error today.

Unblocks

This contract unblocks the following tasks: