Skip to content

[PKI release] Sync current main into certificates - #80

Merged
arvindh123 merged 5 commits into
certificatesfrom
agent/sync-main-for-pki-release
Aug 12, 2026
Merged

[PKI release] Sync current main into certificates#80
arvindh123 merged 5 commits into
certificatesfrom
agent/sync-main-for-pki-release

Conversation

@arvindh123

@arvindh123 arvindh123 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Purpose

Resolve the certificates → main release conflict by merging current main into certificates before final review.

Preserved work

From main (5ccbc779bbe3feaf9790188e541164630519bfbe):

  • broker auth callout and vendored broker proto;
  • the vendored-proto CI drift check;
  • current documentation/R2 publishing changes;
  • current build/proto tooling and repository agent guidance.

From certificates (7bf4cf328b09801689c4f34bcc86c7a06cfaf58c):

  • the complete Atom-native PKI implementation;
  • PostgreSQL migrations 005–016;
  • all PKI/EST/PKCS#11 integration tests;
  • human/AI release runbook and explicit real PKI smoke step.

Conflict resolution

Git had one content conflict, in src/config.rs, where both branches added adjacent environment loaders. The resolution retains all three independent loaders:

  • enrollment_from_env;
  • pki_lifecycle_from_env;
  • broker_auth_from_env.

The Rust workflow retains both the newer Vendored proto matches upstream step and the complete PKI/SoftHSM/smoke gates.

The combined test matrix also exposed a sub-second boundary in the PR-015 lifecycle fixture: the synthetic expired authority could be assigned not_after <= not_before. The fixture now expires one second before the sweep time, preserving the intended overdue case without constructing an invalid authority interval.

Validation

Validated head: 46bc4286f267f4ad671db1dfd5b831fe17e2459b
Validated tree: af15c05d0a2889fc84e90b906d18b7ddb8b31381

  • Rust run 31580801924
    • vendored broker proto matches upstream;
    • cargo fmt --check;
    • locked Clippy with warnings denied;
    • complete test compilation;
    • every test binary against a fresh PostgreSQL database;
    • populated SoftHSM token backup/restore followed by signing;
    • explicit seven-binary real PKI smoke test.
  • API Docs run 31580801901
    • OpenAPI validation;
    • proto lint and generated-doc verification.
  • git diff --check.
  • No conflict markers remain.

Merge requirement

This PR must be merged with a merge commit, not squash/rebase, so main remains an ancestor of certificates and release PR #79 becomes conflict-free.

ianmuchyri and others added 3 commits August 7, 2026 13:07
* Serve doc images from Cloudflare R2 instead of the repo

Images committed into docs/public/img/ bloated PRs and repo history.
They're now stored in the shared "websites-images" R2 bucket (prefix
"atom-docs") and served through a hand-written Cloudflare Worker
(docs/worker/index.ts) added as `main` alongside the static-assets
binding -- this site is a fully static Next.js export with no server
runtime, so neither @cloudflare/next-on-pages nor
@opennextjs/cloudflare applies.

Authoring is unchanged: MDX content already referenced doc images by
their final /img/... path (no relative-path convention existed here
to begin with), so nothing about writing ![alt](/img/foo.png) needed
to change.

- source.config.ts disables fumadocs' remarkImage plugin, which
  otherwise needs the file on local disk at build time.
- docs/components/doc-image.tsx renders the result as a plain,
  zoomable <img> (fumadocs-ui's ImageZoom wrapping a plain element,
  not next/image) -- no width or height required, so there's nothing
  to keep in sync when images change. ImageZoom's src/alt are passed
  explicitly (not just to the inner <img>), since its zoomed-in view
  reads the image from those props directly, not from `children`.
- Dropped lib/image-dimensions.json and the manifest-sync logic that
  used to live in scripts/publish-image.mjs -- simplified back to
  just upload + purge.

All 88 images have already been uploaded to the real R2 bucket and
spot-checked byte-for-byte against the originals.

* Cache R2-proxied doc images at Cloudflare's edge

env.IMAGES_BUCKET.get() is an R2 binding call, not an HTTP subrequest.
Workers run before Cloudflare's cache in the request pipeline, so a
Response the Worker constructs and returns is never automatically
written into the edge cache, no matter what Cache-Control header is
set on it -- that only happens via explicit Cache API use, or a zone
Cache Rule intercepting it. Neither was happening here, so despite
s-maxage=31536000 being set, every request (every visitor, every edge
location) was a live R2 read.

Fixed by writing responses into the Workers Cache API (caches.default)
after the first R2 read, keyed by the request's own URL unmodified (so
it stays purgeable by the existing purge-by-URL call in
scripts/publish-image.mjs on every upload). This also adds Range/206
support as a side effect: cache.match() automatically serves 206
Partial Content for a Range request against a cached 200 response.

Bumped browser max-age from 300s to 3600s while leaving s-maxage at a
year -- purge-on-publish already invalidates the edge instantly on
every upload, so there's no freshness benefit to a short edge TTL.

This repo already depends on @cloudflare/workers-types (unlike the
sister sites), so Cache/CacheStorage/ExecutionContext are used via
their ambient global types instead of local structural interfaces.

Same fix as absmach/website#178, applied here since this repo's
worker/index.ts uses the identical binding-without-caching pattern.

* Fix CI: pin js-yaml past CVE-2026-59870 (quadratic CPU on !!omap)

pnpm audit --prod --audit-level high was failing in CI on js-yaml
4.3.0, pulled in transitively via fumadocs-mdx (.>fumadocs-mdx>js-yaml).
fumadocs-mdx's own declared range ("^4.1.0") already permits the
patched 4.3.1, so this is pinned via a pnpm.overrides entry rather
than waiting on fumadocs-mdx to bump its lockfile-resolved version
itself.

Also pins sharp and postcss to the versions they were already
resolving to before this change (^0.35.3 and ^8.5.18, both already
past their own advisories' patched thresholds) -- introducing the
js-yaml override alone caused pnpm to re-resolve the whole dependency
graph and land on older, vulnerable versions of both (sharp
0.35.3->0.34.5, plus a stray duplicate postcss@8.4.31 no longer
deduped with the top-level 8.5.18), surfacing 2 more high-severity
findings that don't exist in a plain install of main. Pinning all
three keeps the lockfile at exactly the versions already known safe,
with no other dependency churn (confirmed via lockfile diff).

Pre-existing, unrelated to the R2 image-caching work on this branch --
confirmed the same js-yaml advisory already fails audit on a clean
install of main.
* Serve the broker auth callout directly

Atom implements FluxMQ's fluxmq.auth.v1.AuthService, so a broker can
authenticate clients and authorize topics against Atom with no adapter
service in between.

The proto is vendored verbatim from FluxMQ. Its package line is part of
the wire contract — the path a broker dials is derived from it — so it
must not be renamed. How a topic names an object is configuration
(ATOM_BROKER_TOPIC_TEMPLATE), which keeps any particular deployment's
topic vocabulary out of Atom; an adapter service is still the right
answer where the mapping needs more than a grammar.

Two invariants the implementation rests on:

- Denials are answers, not errors. Every rejection returns a successful
  RPC carrying a false verdict; only infrastructure failure returns a
  gRPC error. A broker wraps this callout in a circuit breaker, and a
  tripped breaker rejects every client connection, so one device
  retrying a stale password must not be able to take the broker's whole
  auth path down. Rate limiting is on that list because it is the
  failure a bad client can trigger at will.

- Tenant comes from the subject, not from config or the topic.
  Authenticate resolves the identifier across tenants and the entity's
  own tenant comes back with it, so the zero-configuration case needs
  no tenant in the topic and no username grammar. A {tenant} template
  segment only scopes alias resolution; it is deliberately not checked
  against the subject's tenant, because cross-tenant grants are
  legitimate and that call belongs to the PDP.

Off by default. It is the only gRPC service here with no bearer token to
check, so it authenticates its caller at the transport via the
listener's mTLS client CA; mounted on a plaintext listener, anything
that can reach the port could authenticate and authorize as any
principal. A startup warning fires if it is enabled without a client CA.

* Admit broker operational topics without the PDP

A broker carries topics that address no object — a health probe such as
hc/<tenant> names nothing Atom can resolve — so no policy could describe
them and every request for one was denied. ATOM_BROKER_TOPIC_ALLOW lists
topics authorized without consulting the PDP.

This is the only authorization bypass in the callout, so it defaults to
empty and its patterns are ordinary MQTT filters: an operator writes the
narrowest shape that covers the operational topic rather than reaching
for a prefix.

A broker '#' covers its position and everything below it, so only a
pattern that is itself '#' there is broad enough to admit it. Letting
'+' match it would silently widen the bypass past what the operator
wrote — hc/+ would admit a subscription to the whole hc subtree.

* Serve the broker callout under a vendor-neutral package

The wire path a broker dials is derived from the proto package, so serving
`fluxmq.auth.v1` put one implementation's name in the public surface of
every peer that speaks the contract — including Atom's. Nothing in the
messages is FluxMQ-specific; the same shape covers MQTT, AMQP, CoAP and
HTTP, and other brokers and providers implement it.

Renaming costs nothing structurally. The Go import path upstream is
derived from the file's location rather than the package, so consumers
keep the same imports and symbols, and only the dialled path changes.

It is still a breaking wire change: a broker dialling the new path
against a service serving the old one gets UNIMPLEMENTED. Atom, the
brokers, and any adapter service have to move together.

* Detect drift in the vendored broker contract

Atom implements a proto it does not own, and nothing rebuilds the copy.
An upstream change was therefore discovered at runtime — as an
UNIMPLEMENTED from a renamed service, or worse, as a field that still
decodes but no longer means what Atom thinks it means.

scripts/check-vendored-proto.sh diffs the vendored file against the ref
pinned in proto/broker/v1/REF, and CI runs it. For that diff to stay
trustworthy the copy has to be byte-identical, so Atom's own notes moved
out of the proto and into VENDOR.md beside it: a check that has to
forgive expected differences stops catching the one that matters.

Vendoring a second proto into the buf module had also broken two things
that nothing here runs, so neither had surfaced:

- buf lint failed on twenty-odd violations in the vendored file. Its
  style is upstream's, and Atom cannot fix it without breaking the
  byte-for-byte match, so it is excluded from lint and breaking.
- buf generate silently replaced apidocs/grpc-reference.md with the
  broker contract, dropping Atom's own gRPC surface from the docs.
  protoc-gen-doc writes one file per invocation, so a second package
  does not extend that file. Generation is now scoped to Atom's protos.

make proto regenerates both outputs. The asymmetry is worth knowing: the
Rust bindings are not checked in — build.rs runs tonic-build into OUT_DIR
on every compile — but grpc-reference.md is, and goes stale silently.

* Correct the package a test's doc comment names

The vendored contract moved to `broker.auth.v1`, but this file still named
`fluxmq.auth.v1` — a package no peer serves any more. It was the last
reference to the old name left in the repository.

Drop the attribution to FluxMQ along with it. Calling the service FluxMQ's
is what the rename set out to undo: the contract is the broker's, and Atom
serves it for whichever broker dials.

* Fix formatting

Signed-off-by: dusan <borovcanindusan1@gmail.com>

---------

Signed-off-by: dusan <borovcanindusan1@gmail.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 12, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
atom-docs 3b61107 Commit Preview URL

Branch Preview URL
Aug 12 2026, 08:49 AM

@arvindh123
arvindh123 marked this pull request as ready for review August 12, 2026 09:06
@arvindh123
arvindh123 merged commit 5347f9d into certificates Aug 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants