Skip to content

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 23 Aug 23:09
· 90 commits to main since this release
6373423

Two contract moves reach a tag here. 0.4.0 was bumped in-tree when M4-5
landed and never tagged, released, or published as an image, so no consumer
could pin it; it ships inside this release rather than being retro-tagged, and
its entry is kept below verbatim. Both moves are additive — a client generated
against 0.3.0 is unaffected by either.

0.5.0 — the org storage quota

M4-3: the per-org storage quota. orgs.max_storage_bytes — the last of
the four org quotas, and the one the milestone never built. It was deferred to
M4-8 ("it needs the object store to meter against") while M4-8's own sizing was
deferred to it ("depends on M4-3's storage-quota shape existing to bound it"),
a deadlock that held for the whole milestone with nothing bounding how much one
org could write. It is broken from this side: the meter counts the bytes that
exist today — an org's rows in Postgres plus its git repos on disk — and gains
the object store as a third term the day there is one, without the ceiling's
shape changing.

Additive: max_storage_bytes joins the PATCH /api/adp/orgs/{orgId} body and
the quotas object on org detail. The latter is a wider shape than the three
counting quotas (OrgStorageQuota), because a measured used can be null and
carries the measured_at that says how stale it is.

Three behaviors worth knowing before setting a ceiling. The meter is
sampled, not synchronous
— a ten-minute tick
(STORAGE_METER_INTERVAL_MS), because measuring is a full scan of the org's
rows in ten tables and cannot live on the trajectory hot path; that interval is
exactly the overshoot an org can achieve past its ceiling. An org that has
never been metered is under quota, not over
— storage_bytes_used is null
until the first tick, and failing closed on null would refuse every write on
every instance for one interval after every restart. And gate-job completion
is never refused
: the gate has already run, so refusing would wedge the job
until the reaper and leave its signed evidence unwritten, blocking any commit
under a gates_green policy — a storage quota turning into a land outage.
Instead the completion lands and its logs are dropped, with the drop recorded
on the operation so the empty logs reads as a decision rather than data loss.

Reads are never refused. An org at its ceiling can still clone what it already
has, because a quota that takes the data hostage is a lockout, not a ceiling.

New gauge: adp_storage_bytes{org}, the first storage metric this server has
had. docs/observability.md §5 amends its own "no per-org labels" position to
say why this one is the exception.

0.4.0 — OIDC login (shipped here, never separately released)

M4-5: OIDC login (#103). The standard authorization-code flow with PKCE,
mapped onto identities through a new external_identities table keyed on
(issuer, subject) — not on email, because Google's sub is the stable
identifier and an address that gets reassigned must not hand over an account.

Additive: two new operations, GET /auth/oidc/start and
GET /auth/oidc/callback. No existing operation changes shape, and both
routes are absent entirely on an instance with no IdP configured, so a client
generated against 0.3.0 keeps working untouched.

Two things worth knowing before enabling it. Auto-provisioning is off by
default
— OIDC_ALLOWED_DOMAINS is empty, and empty means a verified
account with no existing link is refused rather than welcomed. And a login
mints repo:read + repo:write only; admin is not reachable from the
login route by any input
, the same bound POST /api/adp/tokens carries.

ID tokens are verified against Node's own crypto rather than a JWT
dependency, with an allowlist of exactly one algorithm. The negative cases —
algorithm confusion, alg: none, unknown kid, tampered payload, wrong
issuer, wrong audience, expired, replayed nonce — are each a test, because
that is the only thing that makes the trade sound.

Image: ghcr.io/deduva/adp:v0.5.0
Digest: sha256:e6a9191df51d52f2dbfd3d291575ea7ffd4de9f6c31ae64f371c659a34a0ba2c — pin with ADP_IMAGE=ghcr.io/deduva/adp@sha256:e6a9191df51d52f2dbfd3d291575ea7ffd4de9f6c31ae64f371c659a34a0ba2c

Runner image: ghcr.io/deduva/adp-runner:v0.5.0
Runner digest: sha256:4155c5cf82e54487a8b9d0f3d1e74f15261a6b4cad92074bbd9201dff30dfce7