Repository navigation
Conversation
Read the GitHub webhook body as a bounded stream and enforce the size limit on actual bytes, independent of Content-Length. Stream read failures now map to 400 instead of 413. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Bodies over 64 KiB are written to R2 before the Queue message carries their reference. Ingress returns 202 only after both writes succeed and deletes the object on a failed enqueue. A 14 day lifecycle rule removes orphans. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Bodies are encrypted with AES-256-GCM before they enter the Queue or R2. The envelope records the algorithm, key id, and IV; payloadSha256 remains the plaintext digest. The delivery id is bound as additional authenticated data so ciphertext cannot be replayed under another delivery. The key and key id come from Worker secrets and are validated as config. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Oversized, non-JSON, and unsupported-event deliveries now return 202 and are dropped with a log line, so GitHub records them as received. Only infrastructure failures on deliveries Janitor would process return 503. Missing headers and invalid signatures still return 400 and 401. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The cluster Worker consumes the GitHub events queue and records each delivery in a Neon journal table inside one transaction, acknowledging only after commit or duplicate. Overflow payloads are read from R2 and deleted after the journal succeeds. Malformed envelopes and missing R2 objects go to a dead-letter queue with content-safe diagnostics. The journal stores ciphertext; nothing in the consumer decrypts. Queue and bucket resources move to shared exports so both Workers bind the same resources. A testcontainers-backed Postgres test covers the journal store and skips when no container runtime is present. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adopt the effect tsgo recommended lint preset, exclude .direnv and node_modules from test discovery, and inline @effect/sql-pg and @effect/platform-node in vitest so they share one effect instance with the tests. The journal test now uses FileSystem and Path from effect and always expects a container runtime. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The journal transaction also writes an outbox request for ProjectGitHubWebhookV1 keyed by delivery id. A dispatcher claims due rows with a bounded lease and fencing token, submits the workflow, and marks rows accepted; failed submissions release with backoff. The consumer dispatches after acknowledging and a cron-woken singleton recovers overdue rows. The workflow decrypts the journaled payload with the shared key, decodes it against the domain event schemas, and records projected, unsupported, or failed on the delivery. Read-model writes come next. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Neon tables for installations, repositories, labels, entities, pull request details, and label assignments. Writes are fenced by GitHub's updated_at and the journal sequence. The pull request event schema gains state, merged, updated_at, labels, and the closed action; the earlier Model classes are replaced by record schemas. Part of a series; the projection wiring lands with the Worker merge. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
App JWT signing for PKCS#1 or PKCS#8 keys, cached installation tokens with one refresh after a 401, and a Neon-backed budget with fenced leases, per-priority reserves, and cooldowns from Retry-After or an exhausted limit. Every GitHub call goes through one client that records rate headers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One target row per scope with generation counters, a five second debounce, and compare-and-set completion that yields exactly one follow-up for work that arrives mid-run. The installation inventory workflow verifies the installation, pages its repositories, marks unlisted ones suspect, and sleeps on uniquely named durable clocks when rate limited. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Label catalog scans, incremental issue scans with a watermark and ten minute overlap, pull request detail scans, and a targeted refresh workflow that treats a 404 as ambiguous rather than a deletion. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A cron-woken planner creates overdue generations per the repair cadence, staggered per installation, with weekly full repairs. Enabling a repository bootstraps its tracks. Uninstall or removal schedules a purge after a seven day grace that wipes ciphertext, titles, bodies, and label names while keeping identities. A freshness function derives projected, verified, syncing, stale, or blocked from a target row. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Stored ETags make scan requests conditional and a 304 serves the stored page, which validates only that exact representation. A 304 on a page that was full when stored probes one page further. Cached bodies are ciphertext bound to their request key and are removed by purges. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Merge ingress into the one backend Worker the design describes: the API prefix routes to the webhook layer, probe endpoints stay, and the standalone webhook Worker is removed. This commit also wires the series together: projection applies events to the read model, invalidates sync targets, schedules purges, and records the installation on each delivery. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The webhooks package only served the cluster Worker, so its code moves there: ingress routes and verification under Ingress, the queue and payload store under GitHub, and the payload cipher at the top level. Service identifiers follow. The topology and migration probes, their queue, cron, store, migrations, and endpoints are removed now that real workflows exist; a migration drops the probe table. Database exports are renamed while Alchemy resource ids stay fixed so live resources are not replaced. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The App private key parser accepts a PEM stored on one line with literal newline escapes, as .env files hold it. direnv loads .env and exports the rootless podman variables the Postgres suites need. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Alchemy binds secrets for Config values read while the Worker initializes; the cluster layer builds lazily, so configs read inside layers were never bound and every request failed with ConfigError. The Worker now reads the webhook secret, payload key, and App credentials during init and hands values to the layers. Route errors that know their response, such as a malformed request, now produce that response instead of a 500. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Each projected delivery logs its status and sequence so a deployment can be observed without database access. Malformed requests and unknown routes log as warnings rather than errors. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GitHub rejects requests without a User-Agent header with a 403, and Workers do not add one, so the first installation inventory run failed. Attach janitor.effectful.co to the cluster Worker to match the App webhook URL. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… entity scan Enabling a repository dispatches all three tracks at once, so the pull request track inserted details before the entity scan had committed its rows and died on the foreign key. Skip details for unknown numbers and request a follow-up generation instead. A run that fails inside the workflow engine leaves no trace in sync_target, so treat dispatched runs older than thirty minutes as dead, log every workflow failure and sync outcome, and add operator scripts to enable a repository and request a track generation against a database. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds GET and POST /api/v1/sync for the re-sync button. The summary derives from the sync target table; the request invalidates every active scope in one transaction. The POST trusts only same-origin browsers until Cloudflare Access verification exists. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Build the Foldkit app during deploy and attach its output as the cluster Worker's static assets, so the browser and the API share one origin. The separate website Worker is gone; non-API paths fall through to the ASSETS binding with single-page fallback. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The button sits next to the theme switcher, fetches the sync summary on load, posts a sync-everything request on press, and polls only while a sync is running. It is disabled with a spinning icon while syncing, the tooltip shows the last sync time, and toasts announce start, completion, blocked scopes, and failures. The web app pins the published effect release, so the wire schema is mirrored locally rather than imported from the domain package. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e worker The cluster Worker enrolls in an Access application that admits members of the Effectful-Tech GitHub organization, and a hostname-level bypass keeps the GitHub webhook route outside Access with its signature check. The sync routes verify the Cf-Access-Jwt-Assertion for signature, issuer, audience, expiry, and subject against the team's cached signing keys, refreshing once on an unknown kid. The workers.dev hostname is disabled. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Rules carry stable ids, one entity target, the closed concrete predicate set, and synchronized label references by stable label id. Saves are whole-ruleset, optimistic on the configured revision, validated against the labels synchronization knows, and recorded with the Access issuer and subject as author. GET and PUT on /api/v1/repositories/:id/rules sit behind the Access assertion check; PUT also requires a same-origin request. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Type
Description
Related