feat(decofile): add s3 target to serve decofile over HTTP (off etcd) - #33
Merged
Conversation
Content-heavy sites (e.g. econverse: 5.5MB decofile, 85% inline blog HTML) compress to ~1.02MB brotli+base64 — over the ~1MB etcd ConfigMap limit, so publishes start failing. Add an opt-in `spec.target: s3` that uploads the merged decofile JSON to S3 and points pods at it over HTTP (DECO_RELEASE=https://…) instead of mounting a ConfigMap, removing the etcd ceiling entirely. - CRD: `s3` target enum + Status.ContentHash/S3URL + S3ObjectKey/DeploymentIdOrName helpers - controller: s3sink.go (S3Uploader + reconcileS3) — retrieve → hash-gate → PutObject (raw JSON) → reuse pod hot-reload notifier; inert unless DECOFILE_S3_BUCKET is set - webhook: for target=s3, set DECO_RELEASE to the HTTP URL (no volume mount) and merge the host into DECO_ALLOWED_AUTHORITIES preserving runtime defaults - chart: DECOFILE_S3_* env wired via helm-generator + values.decofileS3 - tests: S3ObjectKey + mergeAllowedAuthorities Runtime serves plain JSON (the HTTP decofile provider does fetch+JSON.parse with no brotli/base64 decode). Reversible: flip target back to configmap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nicacioliveira
previously approved these changes
Jul 21, 2026
ARCHITECTURE.md: cold-start and hot-reload sequence diagrams for target=s3 (Flow 7a/7b), a configmap-vs-s3 comparison table, and the design decisions (no compression, content-hash change detection, shared key derivation, additive DECO_ALLOWED_AUTHORITIES merge). docs/decofile-s3-onboarding.md: operational runbook (mirrors fast-deploy-onboarding.md) — mental model, platform prerequisites, per-site opt-in steps, verification commands, constraints, troubleshooting, rollback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Added documentation:
Infra PR chain for the prerequisites this depends on:
|
service.Spec.Template.Spec.PodSpec is an embedded field of Knative's RevisionSpec, so .Containers is already promoted — .PodSpec.Containers was flagged as a redundant selector by staticcheck in ensureAllowedAuthority. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nicacioliveira
approved these changes
Jul 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Content-heavy sites are hitting the etcd ConfigMap ceiling.
deco-sites/econversehas a 5.5 MB decofile — 85% of it inline blog-post HTML (post.content, 345 posts) — which compresses to ~1.02 MB after brotli+base64, over the ~1.00 MB etcd/ConfigMap limit. Once over, the API server rejects the ConfigMap write and the site can no longer publish. Brotli-5 (our only current mitigation) is exhausted.What
Adds an opt-in
spec.target: "s3"to the Decofile CRD. When set, the operator uploads the merged decofile JSON to S3 and points pods at it over HTTP (DECO_RELEASE=https://…) instead of mounting a ConfigMap — removing the etcd ceiling entirely. ConfigMap stays the default; fully reversible (fliptargetback toconfigmap).This alone fixes econverse regardless of blog content. (A follow-up will move blog HTML out of blocks, but it's an optimization once the ceiling is gone.)
Changes
api/v1alpha1/decofile_types.go):s3in thetargetenum;Status.ContentHash+Status.S3URL;S3ObjectKey(prefix)+DeploymentIdOrName()helpers (the key derivation shared by reconciler and webhook).internal/controller/s3sink.go, new):S3Uploader(env-configured, IRSA/Pod-Identity creds) +reconcileS3— retrieve → content-hash gate (skips unchanged, avoids repo re-download) →PutObjectraw JSON → reuse the existingNotifyPodsForDecofilehot-reload → status. Dispatched before the FastDeploy switch; wired incmd/main.go(nil/inert unlessDECOFILE_S3_BUCKETset).internal/webhook/v1/service_webhook.go): fortarget=s3,injectDecofileHTTPsetsDECO_RELEASEto the HTTP URL (no ConfigMap volume) andensureAllowedAuthoritymerges the host intoDECO_ALLOWED_AUTHORITIES, preserving the runtime defaults.DECOFILE_S3_*env wired viahack/helm-generator/main.go+chart/values.yamldecofileS3:(inert unlessbucketset).S3ObjectKey+mergeAllowedAuthorities.Key design note
The runtime's HTTP decofile provider (
deco/engine/decofile/fetcher.ts) doesfetch → JSON.parsewith no brotli/base64 decode (that path only exists forfile://….bin). So the S3 object is served as plain JSON. S3 has no size limit, so this is fine; gzip +Content-Encodingis a documented deferred optimization (Denofetchauto-gunzips).Deploy prerequisites (not in this PR)
s3:PutObjecton the bucket prefix.configs.decocdn.com, already in the runtime's default trusted-authority list) and setdecofileS3.publicHost.decofileS3.{bucket,region,publicHost}in the operator values (see infra_applications PR).Test
go build ./...,go vet,go test ./api/v1alpha1/,go test -run TestMergeAllowedAuthorities ./internal/webhook/v1/,helm templatewithdecofileS3.bucketset.Decofilewithspec.target: s3on a test site; confirm the operator writess3://…/decofile.jsonand no ConfigMap; confirm the pod getsDECO_RELEASE=https://…and boots reading from S3; confirm hot-reload still updates a running pod.🤖 Generated with Claude Code
Summary by cubic
Adds an opt-in
spec.target: "s3"to serve the merged Decofile JSON from S3 over HTTP, bypassing the etcd/ConfigMap size limit while keeping hot-reload. Default stays ConfigMap; enable via HelmdecofileS3.*and setspec.target: "s3"to switch.New Features
target: "s3";status.contentHashandstatus.s3URL; helpersDeploymentIdOrName()andS3ObjectKey(prefix).S3UploaderfromDECOFILE_S3_*;reconcileS3does retrieve → SHA-256 gate →PutObject(plain JSON) → pod notify; inert unlessDECOFILE_S3_BUCKETis set.target=s3, injectsDECO_RELEASE=https://<host>/<key>without a ConfigMap volume; merges the host intoDECO_ALLOWED_AUTHORITIESwhile preserving defaults.values.decofileS3.{bucket,region,publicHost[,prefix]}wired toDECOFILE_S3_*.docs/decofile-s3-onboarding.md.S3ObjectKeyandmergeAllowedAuthorities.Bug Fixes
PodSpecselector flagged by staticcheck (no behavior change).Written for commit 3a8430c. Summary will update on new commits.