refactor: reduce switchboard to the preview-deployment daemon - #6
Open
luthermonson wants to merge 2 commits into
Open
refactor: reduce switchboard to the preview-deployment daemon#6luthermonson wants to merge 2 commits into
luthermonson wants to merge 2 commits into
Conversation
Split switchboard in two: the webhook receiver moved to ephpm/switchboard-api
(PHP), and this repo becomes the daemon half — a queue worker, not an HTTP
server. It consumes switchboard-api's job files, provisions previews, and
reports to GitHub via the Deployments API.
Removed
- webhook.rs (signature verification + PullRequestEvent) — now in the API.
- The axum HTTP server, /webhook and /health routes, and the webhook secret.
New
- job.rs: schema-1 job deserialization with strict, defense-in-depth validation
(ref/sha/clone-url/pull_ref re-checked before any value reaches git).
- queue.rs: the watcher — scan, hardlink-claim, per-label coalescing, dedup,
dispatch on intent.
- app_auth.rs: in-memory GitHub App token minting; the token type redacts itself
in logs and is never written to disk. Fails closed on a non-0600 App key.
- git_askpass.rs: passes the token to git via GIT_ASKPASS env, never argv/disk.
- site.rs: ports ePHPm's canonical site-key derivation so the vhost dir, the
per-site DB, the temp/session root and the override file all agree with the
key ePHPm derives from the Host header.
- preview.rs: reduced preview_host; preview_label kept (cfg(test)) as the
cross-implementation guard against switchboard-api's PreviewLabel port.
Changed
- deployer.rs: consumes a Job; clones via refs/pull/<n>/head with GIT_ASKPASS
auth; writes the #391 docroot override; teardown now removes the vhost dir,
the per-site DB (+ -wal/-shm/-journal, which live outside the vhost), the
per-vhost temp/session root, and the override file.
- github.rs: Deployments API lifecycle (queued -> in_progress -> success/failure,
inactive on teardown) plus a failure-only PR comment.
- config.rs: drop listen/webhook_secret; add queue, teardown, and fork-policy knobs.
Security fixes called out
- Fork PRs no longer receive operator secrets by default (the old materialize_env
resolved ${secret.NAME} for any fork). Fork deploys are refused unless
--allow-fork-deploy; even then secrets are withheld unless --fork-secrets.
Notes
- build: runs system composer/PHP, not `ephpm php` (issue #400).
- seed: DB seeding must go over HTTP into the running site (documented).
- MSRV bumped to 1.88 (let-chains); msrv CI job pinned to match.
…ux daemon unaffected
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.
What this is
Reduces
ephpm/switchboardto the daemon half of the split we designed: theHTTP/API surface (webhook receiver) now lives in
ephpm/switchboard-api(PHP),and this repo becomes a queue worker that consumes job files, provisions
previews, and reports to GitHub. Built against switchboard-api's
README.md("The job file contract") and
MIGRATION.md.Do not merge — reporting for review. All checks green locally
(
cargo build, 103 tests,cargo clippy --all-targets -- -D warnings,cargo +nightly fmt --check).Removed (moved to switchboard-api)
src/webhook.rs— signature verification +PullRequestEvent. Deleted.axumHTTP server,/webhookand/healthroutes,AppState, and thewebhook secret.
axumis dropped fromCargo.toml.preview_labelis kept (nowcfg(test)) as the Rust half of thecross-implementation guard against switchboard-api's
PreviewLabelport —if either side changes the algorithm, one of the two suites fails.
New modules
job.rsschemais rejected first; then every field that reachesgit/the filesystem (head.ref,head.sha, owner/repo, clone URL,pull_ref) is re-validated against strict patterns. Defense in depth — the queue is the trust boundary now.queue.rsqueue/, hardlink-claim (link(), notrename()), coalesce newest-per-label, dedup, dispatch onintent. Retains failed jobs inclaimed/for inspection.app_auth.rsDebug/Display, is never written to disk. Fails closed if the App key is not0600.git_askpass.rsgitviaGIT_ASKPASSenv, not argv; the helper script holds no secret (reads it from the environment).site.rsRouter::resolve_site+is_valid_site_key) so the vhost dir,<key>.db, the temp/session root and<key>.tomlall match the key ePHPm derives fromHost.Changed
deployer.rs— consumes aJob; clones viarefs/pull/<n>/headfrom thebase repo (works for forks/deleted forks) with
GIT_ASKPASSauth; writesthe #391
<key>.tomldocroot override; teardown now removes the vhost dir,the per-site DB (
<key>.db+-wal/-shm/-journal, which live outside thevhost), the per-vhost temp/session root, and the override file.
github.rs— Deployments API lifecycle: create Deployment as the firstaction →
queued→in_progress→success(withenvironment_url) /failure;inactiveon teardown. Failed builds also post a marker'd PR commentwith the log.
config.rs— droppedlisten/webhook_secret; added the queue, teardown(
sqlite_dir,site_overrides_dir,vhost_temp_base,sites_domain_suffix),and fork-policy knobs.
main.rs— orchestrator + queue loop, graceful shutdown on SIGTERM/Ctrl-C.Security fix (a real hole in today's code)
The pre-split
materialize_envresolved operator${secret.NAME}into anyfork PR's preview environment — building untrusted code with your secrets. Fixed:
--allow-fork-deploy(a second gate overswitchboard-api's
SWITCHBOARD_ALLOW_FORKS);--fork-secrets;Design decisions forced by the constraints
ephpm php) —build:and the implicitcomposer installrun the system composer/PHP (--composer), neverephpm php. Documented.seed:children have no$_SERVERDBcreds; documented that DB seeding must go over HTTP into the running site, as
wordpress-sampledoes.second one (the #290/#291 anti-pattern).
--sites-domain-suffixmust matchePHPm's; default (unset) names the vhost dir by the full FQDN.
the container path. The daemon reproduces it and removes by the unique
<key>-prefix as a fallback, since the hash could differ across processes(different
TMPDIR, or a std-hasher change). The DB/override/vhost dir — thepersistent leaks — are named deterministically and don't depend on the hash.
rust-versionand themsrvCIjob bumped from 1.85 to 1.88.
Tests
103 passing, covering: job parse/validation (schema rejection, argument-injection
refs, bad SHA, off-host clone URL, pull_ref mismatch, fork fallback); queue claim
/dedup/coalescing (newest-per-label, teardown-supersedes-deploy, retain-on-fail,
quarantine-invalid, no-reprocess-of-claimed); teardown completeness (DB + temp +
override + vhost all removed, prefix-fallback for a mismatched digest); the
fork-secret withholding; canonical site-key parity; token redaction; askpass
never leaking into argv.