Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a6c5156
feat(release-service): add Access service control
ascorbic Aug 27, 2026
a9cc1c9
feat(release-service): add publication product flow
ascorbic Aug 27, 2026
365a0e1
fix(registry-client): parse post-expiry intent updates
ascorbic Aug 28, 2026
24b4774
fix(release-service): terminate final snapshot failures
ascorbic Aug 28, 2026
e5e281e
fix(release-service): recover publication attempt re-entry
ascorbic Aug 28, 2026
b1c816b
fix(release-service): preserve idempotency across run attempts
ascorbic Aug 28, 2026
68a182f
fix(release-service): preserve OAuth error type guard
ascorbic Aug 28, 2026
b797c51
fix(release-service): isolate OAuth authorization state
ascorbic Aug 28, 2026
36fc4ec
fix(release-service): resume early approval wakes
ascorbic Aug 28, 2026
b8d745a
style(release-service): format OAuth state test
ascorbic Aug 28, 2026
fb066ed
fix(release-service): unify operator control routes
ascorbic Aug 28, 2026
47fae9f
fix(release-service): validate OAuth redirect code units
ascorbic Aug 28, 2026
7d8d64b
test(release-service): use signed profile in publication recovery
ascorbic Aug 28, 2026
0ab9990
feat(registry-client): define delegated release source records
ascorbic Aug 28, 2026
4e43981
fix(release-service): reject non-source release input
ascorbic Aug 28, 2026
9f01267
feat(release-service): materialize release artifact URLs
ascorbic Aug 28, 2026
2d9432b
feat(release-service): stage publication artifacts
ascorbic Aug 28, 2026
7d19f43
test(release-service): align materialization intent identity
ascorbic Aug 28, 2026
4dfbd5d
feat(release-service): stage publisher blob uploads
ascorbic Aug 28, 2026
fbe136d
fix(release-service): close pre-create operations safely
ascorbic Aug 28, 2026
a560832
fix(release-service): encode screenshot staging keys
ascorbic Aug 28, 2026
6ab2ddc
fix(release-service): replay publication operation leases
ascorbic Aug 28, 2026
99e78de
fix(release-service): enforce publication phase outcomes
ascorbic Aug 28, 2026
0b087c4
fix(release-service): bind publication materialization
ascorbic Aug 28, 2026
4394a89
feat(release-service): publish staged blobs durably
ascorbic Aug 28, 2026
2002046
chore(release-service): format publication layer
ascorbic Aug 28, 2026
6c3d97f
fix(release-service): fully encode staging slots
ascorbic Aug 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/delegated-release-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@emdash-cms/registry-client": minor
"@emdash-cms/plugin-cli": minor
---

Adds typed clients for the experimental delegated release service. `ReleaseServiceClient` submits, polls, and cancels GitHub OpenID Connect release intents, and manages publisher workload policies and retained delegation through a publisher session. `ReleaseServiceOperatorClient` exposes the Cloudflare Access status, pause, suspension, revocation, cancellation, and reconciliation operations.

Both clients validate response envelopes and return stable `ReleaseServiceError` codes with retry metadata. Mutation helpers require idempotency keys, and workload polling requests a fresh token from the configured provider for each call.

The plugin CLI adds `emdash-plugin release submit`, `release status`, and `release cancel` for GitHub Actions jobs. The commands request audience-bound OIDC tokens from the runner, support JSON output, and use the GitHub run identity as the default idempotency key.
66 changes: 66 additions & 0 deletions apps/release-action/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# EmDash delegated release Action

This experimental Action submits a package release record to an EmDash delegated release service. It requests a GitHub OpenID Connect (OIDC) token for each service call, so the workflow does not store a release-service secret.

## Workflow setup

Grant the job permission to request an OIDC token, then pass the publisher DID and generated release record to the Action:

```yaml title=".github/workflows/release.yml"
name: Release plugin

on:
workflow_dispatch:

permissions:
contents: read
id-token: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Build release record
run: pnpm build:release-record --output release.json

- name: Publish through EmDash
id: release
uses: emdash-cms/emdash/apps/release-action@<exact-commit>
with:
service-url: https://release.example.com
publisher-did: did:web:publisher.example.com
release-file: release.json
```

Replace the example service URL, publisher DID, build command, and exact commit with values for your publisher. Pin the Action to an exact commit while the delegated release protocol remains experimental.

The release record must conform to `com.emdashcms.experimental.package.release`. The service validates its package, version, artifact, declared access, and provenance before publication.

## Inputs

| Input | Required | Default | Purpose |
| ----------------------- | -------- | -------------- | ---------------------------------------------------------------------------------------------- |
| `service-url` | Yes | — | HTTPS origin of the delegated release service. |
| `publisher-did` | Yes | — | DID that owns the package profile and release records. |
| `release-file` | Yes | — | JSON file containing the package release record. The path must stay inside `GITHUB_WORKSPACE`. |
| `idempotency-key` | No | Current run ID | Stable key used to replay the same submission. |
| `poll-interval-seconds` | No | `5` | Delay between intent status requests. |
| `timeout-minutes` | No | `30` | Maximum polling time. |
| `wait-for-approval` | No | `false` | Continue polling when the intent reaches `awaiting_approval`. |

The default idempotency key is stable across attempts of one GitHub run. Set `idempotency-key` when separate runs or jobs must replay the same submission identity.

## Outputs

| Output | Value |
| -------------- | -------------------------------------------------- |
| `intent-id` | Release intent ULID. |
| `state` | Published, terminal, or `awaiting_approval` state. |
| `approval-url` | Approval URL when passkey approval is required. |
| `release-uri` | Published AT URI. |
| `release-cid` | Published record CID. |
| `reason-code` | Stable failure reason for a terminal intent. |

With the default `wait-for-approval: false`, an intent awaiting approval returns successfully with `state` and `approval-url` outputs. Terminal states other than `published` fail the step. Network failures, service pauses, and polling timeouts also fail with a stable client error code.
47 changes: 47 additions & 0 deletions apps/release-action/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: EmDash delegated release
description: Publish an EmDash plugin release through the delegated release service.
author: EmDash
inputs:
service-url:
description: HTTPS origin of the delegated release service.
required: true
publisher-did:
description: Publisher DID that owns the package.
required: true
release-file:
description: Path to the package release record JSON file.
required: true
idempotency-key:
description: Stable key for replaying this submission. Defaults to the GitHub run.
required: false
poll-interval-seconds:
description: Seconds between status requests.
required: false
default: "5"
timeout-minutes:
description: Maximum time to wait for publication or approval.
required: false
default: "30"
wait-for-approval:
description: Continue polling while the intent awaits approval.
required: false
default: "false"
outputs:
intent-id:
description: Release intent ULID.
state:
description: Final or approval-waiting intent state.
approval-url:
description: Approval URL when human approval is required.
release-uri:
description: Published AT URI.
release-cid:
description: Published record CID.
reason-code:
description: Stable terminal reason code when publication does not succeed.
runs:
using: node24
main: dist/index.js
branding:
icon: upload-cloud
color: purple
Loading
Loading