Skip to content
Closed
Changes from 1 commit
Commits
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
82 changes: 82 additions & 0 deletions packages/kotlin-sdk/MAVEN_CENTRAL_INFRA_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Infra runbook: the `maven-central` GitHub Environment

This is the **infrastructure/admin half** of enabling Maven Central publishing for
`org.dashj:dash-sdk-android`. The **code half** — the release workflow that gates publishing
behind this environment — is [dashpay/platform#4193](https://github.com/dashpay/platform/pull/4193)
(`.github/workflows/kotlin-sdk-release.yml`). Neither half is complete on its own: the workflow
already restricts every publishing secret to the `environment: maven-central` job, but that boundary
is only airtight once the secrets are actually **scoped to this environment** and the
repository/organization-scoped copies are removed (step 3 below).

Developer-facing context on the release flow itself is in
[`PUBLISHING.md`](./PUBLISHING.md); this file is the checklist for whoever has org-owner /
repo-admin rights on `dashpay/platform`.
Comment on lines +11 to +13

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Linked publishing guide gives the opposite secret-scoping instructions

The linked #4193 head at e22c26e4c1 still has a PUBLISHING.md section telling administrators to install the five credentials at repository/organization scope, not to scope them environment-only, and to retain the broad copies. Its final workflow no longer needs that access because the secret check was moved into maven-central-deploy, but this new runbook directs readers to the contradictory guide before requiring the opposite setup in step 3. Because this PR is explicitly the infrastructure half of #4193, the combined instructions must consistently require environment-only secrets and removal of this repository's access to broad copies; otherwise administrators can leave the credentials available to unprotected jobs and the stated security goal remains unresolved.

source: ['codex']

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in cf624f5Linked publishing guide gives the opposite secret-scoping instructions no longer present.

Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.


All steps are in **GitHub → `dashpay/platform` → Settings → Environments**. Developers cannot
create environments or environment secrets — this requires an admin.

---

## 1. Create the environment

Settings → Environments → **New environment** → name it exactly **`maven-central`**
(lowercase, hyphen). It must match the `environment:` value in `kotlin-sdk-release.yml` verbatim —
any mismatch means the deploy job runs **without** the gate.

## 2. Add the approval gate

- Enable **Required reviewers** and add the person/team allowed to approve a Maven Central publish
(e.g. the release owners). One reviewer is sufficient; two is fine. This is what makes the
`maven-central-deploy` job pause for human approval before it can publish irrevocably.
- Recommended: **Deployment branch and tag policy → Selected branches and tags → Add rule → Tag →
`kotlin-sdk-v*`**. This restricts the environment (and therefore the credentials) to real
Kotlin-SDK release tags only.
- Optional: a short **Wait timer** for a cooling-off window. Not required.

## 3. Scope the publishing secrets to the environment (and delete the repo/org copies)

Add each of the following as an **Environment secret on `maven-central`**
(Environment → Environment secrets → Add secret), using the same values currently stored at the
repository/organization level:

| Secret name | What it is |
|---|---|
| `JRELEASER_MAVENCENTRAL_SONATYPE_USERNAME` | Central Portal / Sonatype username (or token username) |
| `JRELEASER_MAVENCENTRAL_SONATYPE_PASSWORD` | Central Portal / Sonatype password (or token) |
| `JRELEASER_GPG_SECRET_KEY` | ASCII-armored GPG **private** signing key |
| `JRELEASER_GPG_PASSPHRASE` | passphrase for that GPG key |
| `JRELEASER_GPG_PUBLIC_KEY` | GPG public key (not sensitive, but keep it alongside the others) |

Then **delete the repository-scoped and organization-scoped copies of these same five names**
(Settings → Secrets and variables → Actions, and the org secrets list).

> **This deletion is the crux.** As long as a repo/org copy exists, any unprotected job in the repo
> can still read the credentials, and the security finding that motivated #4193 is not actually
> closed. After this step, only the approved `maven-central` job can see them.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Do not unconditionally delete shared organization secrets

An organization-scoped secret can be shared with other repositories. Deleting it from the organization would revoke those repositories' credentials and could break their publishing workflows. The required security invariant is that dashpay/platform cannot access a broad copy, so delete repository-scoped copies and remove this repository from each organization secret's access policy; delete an organization secret entirely only when no other repository uses it.

Suggested change
Then **delete the repository-scoped and organization-scoped copies of these same five names**
(Settings → Secrets and variables → Actions, and the org secrets list).
> **This deletion is the crux.** As long as a repo/org copy exists, any unprotected job in the repo
> can still read the credentials, and the security finding that motivated #4193 is not actually
> closed. After this step, only the approved `maven-central` job can see them.
Then **delete the repository-scoped copies and remove `dashpay/platform`'s access to any
organization-scoped copies of these same five names** (Settings → Secrets and variables → Actions,
and the organization secrets list). Delete an organization secret entirely only if no other
repository uses it; otherwise update its repository access policy to exclude `dashpay/platform`.
> **Removing this repository's access is the crux.** As long as a repository-scoped copy exists, or
> an organization-scoped copy remains available to `dashpay/platform`, an unprotected job in this
> repository can still read the credentials and the security finding that motivated #4193 is not
> actually closed. After this step, only the approved `maven-central` job can see them.

source: ['codex']

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in cf624f5Do not unconditionally delete shared organization secrets no longer present.

Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.

>
> `JRELEASER_GPG_PUBLIC_KEY` is a public key and not itself sensitive, but move it too so all five
> live in one place and the build job has no reason to touch the environment.

## 4. Verify

1. Push a real `kotlin-sdk-vX.Y.Z` tag (or use the workflow's `workflow_dispatch` with an existing
tag). The run should **pause** at the `maven-central-deploy` job showing *"Waiting for review."*
2. It proceeds to publish only after an approved reviewer clicks **Approve and deploy**.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: Manual verification must dispatch from the tag ref

With the recommended environment policy allowing only kotlin-sdk-v* tags, supplying a tag through the workflow's free-form tag input is not enough when the workflow run itself is dispatched from a branch. GitHub evaluates environment deployment policies against the run's github.ref, not the input or the ref later checked out by the job, so the deploy job will be rejected before reaching the approval gate. Document that manual verification and emergency reruns must dispatch the workflow at the tag ref.

Suggested change
1. Push a real `kotlin-sdk-vX.Y.Z` tag (or use the workflow's `workflow_dispatch` with an existing
tag). The run should **pause** at the `maven-central-deploy` job showing *"Waiting for review."*
2. It proceeds to publish only after an approved reviewer clicks **Approve and deploy**.
1. Push a real `kotlin-sdk-vX.Y.Z` tag. To verify with `workflow_dispatch`, dispatch the
workflow **at that tag ref**, for example:
`gh workflow run kotlin-sdk-release.yml --ref kotlin-sdk-vX.Y.Z -f tag=kotlin-sdk-vX.Y.Z`.
Supplying only the `tag` input while dispatching from a branch does not satisfy a tag-only
environment policy. The run should **pause** at the `maven-central-deploy` job showing
*"Waiting for review."*
2. It proceeds to publish only after an approved reviewer clicks **Approve and deploy**.

source: ['codex']

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in cf624f5Manual verification must dispatch from the tag ref no longer present.

Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.

3. Confirm the `build-and-release` job has **no** access to the five secrets — after #4193 it does
not reference them at all; its only context token is the auto-provided `GITHUB_TOKEN`.

> **Transitional note.** Because the build job can no longer read secrets, the "secrets not
> configured" case is detected only *after* environment approval — the gated job's first step
> (`Check Maven Central publishing secrets`) no-ops the publish when the secrets are absent. Until
> step 3 is done, a valid release tag will therefore prompt a reviewer for what becomes a no-op.
> Completing step 3 makes this moot. The partial-secret hard-fail guard is preserved, just
> relocated behind the gate.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Blocking: Pre-migration approvals will not necessarily no-op

The transitional note says that approving a valid release before step 3 is complete results in a no-op because environment secrets are absent. GitHub's secret precedence does not restrict an environment job to environment-scoped secrets: an environment secret overrides a same-named repository or organization secret, but when that environment secret is absent, an accessible repository or organization secret still resolves through ${{ secrets.NAME }}. Lines 39-40 state that the five credentials currently exist at those broader scopes, and the linked #4193 workflow at head 6bf611a reads all five ordinary secrets.* values in the approved maven-central-deploy job. Therefore, after approval, the all-or-nothing check can succeed and perform an irrevocable Maven Central publish. The note must warn reviewers that broad copies remain effective until this repository's access is revoked.

Suggested change
> **Transitional note.** Because the build job can no longer read secrets, the "secrets not
> configured" case is detected only *after* environment approval — the gated job's first step
> (`Check Maven Central publishing secrets`) no-ops the publish when the secrets are absent. Until
> step 3 is done, a valid release tag will therefore prompt a reviewer for what becomes a no-op.
> Completing step 3 makes this moot. The partial-secret hard-fail guard is preserved, just
> relocated behind the gate.
> **Transitional note.** The secret check runs only after environment approval. Until step 3 is
> complete, repository- or organization-scoped copies that remain available to `dashpay/platform`
> also resolve in the `maven-central` job, so approving a valid release tag can publish; it is **not**
> a no-op test. Complete step 3 before approving a verification run. If all five secrets are genuinely
> unavailable, the gated job no-ops, while a partially available set hard-fails.

source: ['codex']

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 375941ePre-migration approvals will not necessarily no-op no longer present.

Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.


---

### One-line summary for the ticket

> On `dashpay/platform`, create a `maven-central` GitHub Environment with required reviewers (and a
> `kotlin-sdk-v*` tag policy), add the five `JRELEASER_*` publishing secrets as **environment**
> secrets, and **delete the repository/organization-scoped copies** of those same secrets. Code
> side: dashpay/platform#4193.
Loading