Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 2 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,10 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max

# Best-effort: a PAT without repo:write_metadata returns 403 here.
# The image push above is already committed, so don't fail the job.
# Requires DOCKERHUB_TOKEN with the `repo:write_metadata` scope.
# See docs/RELEASE.md §1 for the operator setup steps.
- name: Sync Docker Hub description
uses: peter-evans/dockerhub-description@v5
continue-on-error: true
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
### Security
- **Portal CORS preflight gains a deny-cache.** `PortalCorsMiddleware.HandlePreflightAsync` now caches both allow and deny outcomes for the same TTL as the per-app signing-key lookup (default 60 s). Browsers don't cache rejected preflights, so before this change every `OPTIONS` against `/api/v1/portal/*` from a disallowed origin re-scanned the portal-enabled application set and the in-process JSON deserialize loop — a low-effort DB hammer vector. New regression test `Preflight_Deny_Decision_Is_Cached_Within_Ttl` pins the behaviour.

### Build
- **`release.yml` removes the `continue-on-error` workaround on the `Sync Docker Hub description` step.** The step previously soft-failed on every release because `DOCKERHUB_TOKEN` lacked the `repo:write_metadata` scope; the workaround masked the underlying secret-misconfiguration. The token now needs the documented scope set (see `docs/RELEASE.md` §1) so a release that loses the Docker Hub overview sync surfaces as a hard CI failure instead of vanishing silently.

### Documentation
- **`docs/RELEASE.md` §1 — `DOCKERHUB_TOKEN` scope requirement made explicit.** The secret table now lists the three required scopes (`repo:read`, `repo:write`, `repo:write_metadata`) and where to set them on Docker Hub. Avoids the recurring "release ran but Docker Hub overview is stale" debugging session.
- **ADR-004 — Portal Signing Key Storage: Plaintext + Instant Invalidation, No Rotation Grace.** Locks in the v0.2.0 decisions around per-app `PortalSigningKey` at-rest storage (`varchar(64)` plaintext, mirroring the existing `SigningSecret` pattern), the no-grace rotation lifecycle (instant local invalidation, TTL-bounded multi-replica), and the one-shot reveal contract on `enable` / `rotate`. Documents the threat model, alternatives considered (`pgcrypto`, rotation grace, external KMS), and the explicit revisit triggers (compliance regimes, recurring host-integration friction, key-reuse beyond JWT verification).
- **ADR-005 — Portal CORS Preflight Deny-Cache TTL.** Locks in PR #104's choice to cache both allow and deny outcomes for `PortalAuth:LookupCacheTtlSeconds` (default 60s), reusing the existing per-app cache window for symmetry. Documents why no synchronous invalidation hook from `PUT /portal/origins` (the cache key is origin-scoped, the operator action is app-scoped — the bookkeeping cost outweighs the ≤60s staleness), and what would justify revisiting (operator UX complaints, memory-pressure attacks via origin enumeration, richer CORS semantics).
- **`docs/API.md` §3.8 — Portal API (Customer-Facing JWT).** Replaces the doc-drift gap surfaced by the v0.2.0 audit (where the portal API surface existed in code and CHANGELOG but was absent from `docs/API.md`). Covers HS256 JWT contract, capability scopes, per-app CORS, rate-limit partition reuse, cross-tenant `404` semantics, every route under `/api/v1/portal/*`, the dashboard portal-admin routes, the portal-specific error code table, and an end-to-end Node.js (`jose`) + cURL probe.
Expand Down
2 changes: 1 addition & 1 deletion docs/RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Add these secrets:
| Secret | Description |
|--------|-------------|
| `DOCKERHUB_USERNAME` | Docker Hub username |
| `DOCKERHUB_TOKEN` | Docker Hub access token |
| `DOCKERHUB_TOKEN` | Docker Hub Personal Access Token. **Required scopes: `repo:read`, `repo:write`, `repo:write_metadata`.** The third scope is what lets the `Sync Docker Hub description` step in `release.yml` push the README to the Hub overview; without it that step fails with `403`. Create the token at Docker Hub → Account Settings → Personal Access Tokens. |
| `NUGET_API_KEY` | NuGet.org API key |
| `NPM_TOKEN` | npm automation token (for portal package — see Section 6) |

Expand Down
Loading