openapi: 3.0.3 info: title: CDP → Akrites External Advisories Stewardship Actions API version: 0.1.0 description: 'Read-only external API exposing CDP package security data to the Akrites service. Authenticated via Auth0 M2M client-credentials — CDP only verifies the resulting access token; the assertion exchange happens entirely between Akrites and Auth0. Packages, Advisories and Contacts endpoints are implemented. Blast Radius is specced separately and not yet built. TODO: scopes below (read:packages, read:stewardships) are the existing internal CDP UI scopes, reused here for now. Swap for a dedicated cdp:packages:read scope once Akrites gets its own Auth0 M2M scopes per the akrites-external draft contract. ' servers: - url: https://cm.lfx.dev/api/v1 description: Production security: - M2MBearer: - read:packages - read:stewardships tags: - name: Stewardship Actions description: Admin-initiated stewardship mutations. paths: /stewardships/open: post: operationId: openStewardship summary: Open a package for stewardship description: 'Transitions the stewardship status to `open`, marking the package as available for claiming. If a stewardship row does not exist yet, one is created. If the stewardship is already `open`, this is a no-op (idempotent). ' tags: - Stewardship Actions security: - M2MBearer: - write:stewardships requestBody: required: true content: application/json: schema: type: object required: - purl - actor properties: purl: type: string description: Package URL (must start with `pkg:`). example: pkg:npm/lodash actor: $ref: '#/components/schemas/ActorInput' example: purl: pkg:npm/lodash actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: https://avatars.githubusercontent.com/u/12345 responses: '200': description: Stewardship opened (or already open). content: application/json: schema: type: object required: - stewardship properties: stewardship: $ref: '#/components/schemas/StewardshipRecord' example: stewardship: id: '42' packageId: '1234' status: open origin: opened_for_claim version: 1 openedAt: '2026-06-15T10:00:00Z' lastStatusAt: '2026-06-15T10:00:00Z' inactiveReason: null createdAt: '2026-06-15T10:00:00Z' updatedAt: '2026-06-15T10:00:00Z' '400': description: Validation error (e.g. missing or invalid purl). content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: Missing or invalid bearer token. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Insufficient scopes. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Package not found. content: application/json: schema: $ref: '#/components/schemas/Error' /stewardships/{id}/assign: post: operationId: assignSteward summary: Assign a steward to a stewardship description: 'Assigns a user as a steward with the given role. If the user is already an active steward, the role is updated (soft-delete + re-insert). Returns the unchanged stewardship record and the full active stewards list after the operation. ' tags: - Stewardship Actions security: - M2MBearer: - write:stewardships parameters: - name: id in: path required: true schema: type: integer description: Stewardship ID. example: 42 requestBody: required: true content: application/json: schema: type: object required: - steward - actor properties: steward: type: object required: - userId - role description: 'The user to assign as steward. `username` and `displayName` must be provided together or both omitted — sending one without the other returns 400. ' properties: userId: type: string minLength: 1 description: Auth0 sub of the user to assign as steward. example: abc123 username: type: - string - 'null' description: LFX username of the steward. Must be provided together with `displayName`. example: joanagmaia displayName: type: - string - 'null' description: Full display name of the steward. Must be provided together with `username`. example: Joana Maia role: type: string enum: - lead - co_steward note: type: string minLength: 1 description: Optional free-text note for the activity log. moveToAssessing: type: boolean default: false description: 'If true, atomically transitions the stewardship status to `assessing` in the same transaction as the assignment. Use for the "Assign & move to Assessing" action to avoid a second round-trip. ' actor: $ref: '#/components/schemas/ActorInput' example: steward: userId: abc123 role: lead moveToAssessing: true actor: userId: auth0|xyz username: gaspergrom displayName: Gašper Grom avatarUrl: https://avatars.githubusercontent.com/u/12345 responses: '200': description: Steward assigned. content: application/json: schema: type: object required: - stewardship - stewards properties: stewardship: $ref: '#/components/schemas/StewardshipRecord' stewards: type: array items: $ref: '#/components/schemas/StewardRecord' example: stewardship: id: '42' packageId: '1234' status: open origin: opened_for_claim version: 1 openedAt: '2026-06-15T10:00:00Z' lastStatusAt: '2026-06-15T10:00:00Z' inactiveReason: null createdAt: '2026-06-15T10:00:00Z' updatedAt: '2026-06-15T10:00:00Z' stewards: - id: '7' stewardshipId: '42' userId: abc123 role: lead assignedAt: '2026-06-15T10:05:00Z' assignedBy: xyz789 '400': description: Validation error (e.g. invalid role). content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: Missing or invalid bearer token. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Insufficient scopes. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Stewardship not found. content: application/json: schema: $ref: '#/components/schemas/Error' /stewardships/{id}/escalate: post: operationId: escalateStewardship summary: Escalate a stewardship description: 'Transitions the stewardship to `escalated` status and logs the chosen resolution path in the activity log. Optional free-text notes can be included. ' tags: - Stewardship Actions security: - M2MBearer: - write:stewardships parameters: - name: id in: path required: true schema: type: integer description: Stewardship ID. example: 42 requestBody: required: true content: application/json: schema: type: object required: - resolutionPath - actor properties: resolutionPath: $ref: '#/components/schemas/EscalationResolutionPath' notes: type: string minLength: 1 description: Optional free-text notes for the activity log. example: Contacted maintainer, no response after 30 days. actor: $ref: '#/components/schemas/ActorInput' example: resolutionPath: right_of_first_refusal notes: Contacted maintainer, no response after 30 days. actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: https://avatars.githubusercontent.com/u/12345 responses: '200': description: Stewardship escalated. content: application/json: schema: type: object required: - stewardship properties: stewardship: $ref: '#/components/schemas/StewardshipRecord' example: stewardship: id: '42' packageId: '1234' status: escalated origin: opened_for_claim version: 1 openedAt: '2026-06-15T10:00:00Z' lastStatusAt: '2026-06-15T11:00:00Z' inactiveReason: null createdAt: '2026-06-15T10:00:00Z' updatedAt: '2026-06-15T11:00:00Z' '400': description: Validation error (e.g. invalid resolutionPath). content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: Missing or invalid bearer token. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Insufficient scopes. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Stewardship not found. content: application/json: schema: $ref: '#/components/schemas/Error' /stewardships/{id}/status: patch: operationId: updateStewardshipStatus summary: Update stewardship status description: 'Updates the stewardship status and logs a `state_changed` activity entry. Valid target statuses: `assessing`, `active`, `needs_attention`, `blocked`, `inactive`. When transitioning to `inactive`, `inactiveReason` is required. ' tags: - Stewardship Actions security: - M2MBearer: - write:stewardships parameters: - name: id in: path required: true schema: type: integer description: Stewardship ID. example: 42 requestBody: required: true content: application/json: schema: type: object required: - status - actor properties: status: type: string enum: - assessing - active - needs_attention - blocked - inactive inactiveReason: type: string enum: - quarterly_cadence_missed - stepped_down - no_longer_critical description: Required when `status` is `inactive`. notes: type: string minLength: 1 description: Optional free-text notes for the activity log. actor: $ref: '#/components/schemas/ActorInput' examples: set_active: summary: Transition to active value: status: active actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: https://avatars.githubusercontent.com/u/12345 set_inactive: summary: Transition to inactive (inactiveReason required) value: status: inactive inactiveReason: stepped_down notes: Steward stepped down voluntarily. actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: https://avatars.githubusercontent.com/u/12345 set_blocked: summary: Transition to blocked value: status: blocked notes: Waiting on upstream maintainer response. actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: https://avatars.githubusercontent.com/u/12345 responses: '200': description: Status updated. content: application/json: schema: type: object required: - stewardship properties: stewardship: $ref: '#/components/schemas/StewardshipRecord' example: stewardship: id: '42' packageId: '1234' status: active origin: opened_for_claim version: 1 openedAt: '2026-06-15T10:00:00Z' lastStatusAt: '2026-06-15T12:00:00Z' inactiveReason: null createdAt: '2026-06-15T10:00:00Z' updatedAt: '2026-06-15T12:00:00Z' '400': description: 'Validation error. Includes the case where `status` is `inactive` but `inactiveReason` is missing. ' content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: Missing or invalid bearer token. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Insufficient scopes. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Stewardship not found. content: application/json: schema: $ref: '#/components/schemas/Error' components: schemas: ActorInput: type: object required: - userId description: 'Profile of the actor performing this action. Stored as a snapshot on the activity log. `userId` is required. All other fields are optional and can be null. ' properties: userId: type: string minLength: 1 description: Auth0 sub of the actor. Must match the authenticated user's token sub. example: auth0|abc123 username: type: - string - 'null' minLength: 1 description: LFX username of the actor. example: gaspergrom displayName: type: - string - 'null' minLength: 1 description: Full display name of the actor. example: Gašper Grom avatarUrl: type: - string - 'null' format: uri description: Avatar URL of the actor. example: https://avatars.githubusercontent.com/u/12345 StewardRecord: type: object required: - id - stewardshipId - userId - role - assignedAt properties: id: type: string example: '7' stewardshipId: type: string example: '42' userId: type: string description: Auth0 sub of the assigned steward. example: abc123 username: type: - string - 'null' description: LFX username of the steward. Null if not yet stored. example: joanagmaia displayName: type: - string - 'null' description: Full display name of the steward. Null if not yet stored. example: Joana Maia role: type: string enum: - lead - co_steward assignedAt: type: string format: date-time assignedBy: type: - string - 'null' description: Auth0 sub of the admin who assigned this steward. example: xyz789 EscalationResolutionPath: type: string enum: - right_of_first_refusal - replace_the_dependency - find_vendor_for_lts - consortium_adopts_maintainership - compensating_controls_monitor - namespace_takeover Error: type: object required: - error properties: error: type: object required: - code - message properties: code: type: string example: NOT_FOUND message: type: string example: Stewardship not found. StewardshipRecord: type: object required: - id - packageId - status - origin - version - createdAt - updatedAt properties: id: type: string example: '42' packageId: type: string example: '1234' status: $ref: '#/components/schemas/StewardshipStatus' origin: type: string enum: - auto_imported - self_claimed - assigned - opened_for_claim version: type: integer example: 1 openedAt: type: - string - 'null' format: date-time lastStatusAt: type: - string - 'null' format: date-time inactiveReason: type: - string - 'null' enum: - quarterly_cadence_missed - stepped_down - no_longer_critical - 'null' resolutionPath: description: Set on `escalated` status. Null for all other statuses. oneOf: - $ref: '#/components/schemas/EscalationResolutionPath' - type: 'null' statusNote: type: - string - 'null' description: Free-text note for the current status. Set by escalate or updateStatus. Null on open. example: Contacted maintainer, no response after 30 days. createdAt: type: string format: date-time updatedAt: type: string format: date-time StewardshipStatus: type: string enum: - unassigned - open - assessing - active - needs_attention - escalated - blocked - inactive securitySchemes: M2MBearer: type: oauth2 description: 'Auth0 machine-to-machine client-credentials flow. Akrites exchanges its client ID/secret with Auth0 for a JWT and sends it as `Authorization: Bearer `; CDP only verifies the resulting token. ' flows: clientCredentials: tokenUrl: https://linuxfoundation.auth0.com/oauth/token scopes: read:packages: Read package detail read:stewardships: Read package stewardship data read:maintainer-roles: Read security contacts (interim scope for Contacts; see the Contacts tag)