Release: unified Docker setup, EC2 deploy pipeline, optional passkeys - #4
Merged
Conversation
Password sign-in (MUDDA_OWNER_PASSWORD) now works whenever the secret is configured, regardless of passkeys. Passkeys become an optional convenience for biometric/device sign-in rather than a required step. - Rename BootstrapPassword -> OwnerPassword; enabled? is now just configured? - Remove the PasswordLockdown middleware, the forced PasskeyEnrollment concern, the welcome-letter flow, and the "can't delete your only passkey" guard - Sign-in page shows the password form and, when passkeys exist, a passkey button - Reframe seeds, auth:reset, and docs; the seed still warns when there is neither a password secret nor a passkey (no way to sign in) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploys to a single EC2 box over Docker. GitHub Actions builds the production image, pushes to GHCR, and deploys via AWS SSM (OIDC — no static AWS keys, no SSH). Caddy terminates TLS with auto-HTTPS for an *.sslip.io name, so no DNS is required and passkeys work again over HTTPS. - Dockerfile: multi-stage production image (assets precompiled, runs non-root) - docker-compose.prod.yml + deploy/Caddyfile: app behind Caddy, EBS-backed /rails/storage, only 80/443 published - deploy/remote-deploy.sh: render runtime .env, pull from GHCR, db:prepare, up -d - .github/workflows/deploy.yml: build+push GHCR, OIDC to AWS, ssm send-command - DEPLOY.md: box prep, GitHub Environment secrets/vars, backups, rollback - Docs-sync: AGENTS.md/CLAUDE.md no longer claim "no deploy tooling" App secrets come from GitHub Actions secrets injected at deploy time (never at build); they transit SSM command history — documented, with an SSM Parameter Store upgrade path noted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ycle) (#3) * Unify dev and prod Dockerfiles into one multi-stage build Replace the separate Dockerfile.dev with a single multi-stage Dockerfile whose stages share one base, so the Ruby version and bundler path can't drift between environments: base -> dev (development target) base -> prod-base -> build -> production (default target) - docker-compose.yml builds `target: dev`. - deploy.yml builds `target: production` explicitly (it's also the last stage, so a plain `docker build` still yields the prod image). - Sync DOCKER.md, DEPLOY.md, AGENTS.md, CLAUDE.md to the named targets. Both targets pass `docker buildx build --check` with no warnings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Unify the dev and prod container entrypoint and DB lifecycle Dev now prepares the database as an explicit pre-boot step: `make setup`/`make fresh` run `db:prepare` in a throwaway container before `up -d`, exactly like the production deploy (deploy/remote-deploy.sh). With migration no longer happening on boot, dev and prod share one dumb entrypoint that only clears a stale Puma pidfile. - Delete bin/docker-entrypoint-dev; the dev image now uses the same /rails/bin/docker-entrypoint as production. - Makefile: add a `prepare` target; setup/fresh do build -> prepare -> up -d. - docker-compose.yml: drop the redundant `command` override so dev uses the image CMD, like prod. - Dockerfile: dev stage points at the shared entrypoint; align its CMD. - DOCKER.md: document the shared entrypoint and the pre-boot prepare. Behavior change: a failed db:prepare (e.g. a broken db/seeds.rb) now aborts `make setup`/`fresh` instead of warning and booting without seeds — matching the production deploy's fail-fast. Verified end to end: `make fresh` runs db:prepare (with seeding) in an ephemeral container before mudda-web-1 starts, then the app boots healthy (200 on /up). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The box is a generic VPS (not EC2), so the SSM Run Command transport no longer applies. Swap only the delivery mechanism — the image build, compose stack, Caddy TLS, and on-box deploy script are provider-neutral and unchanged. Deploy job now renders the runtime .env in the runner, scp's the compose file, Caddyfile, and .env to /srv/mudda over the encrypted SSH channel, then runs remote-deploy.sh. Secrets travel via printf %s (no shell expansion) into a 0600 file, never on a command line. The GHCR image is public, so remote-deploy.sh drops docker login and base64 and just pulls, migrates, and rolls the stack. The deploy user (saksham) owns /srv/mudda and is in the docker group, so no step needs sudo; its uid 1000 matches the container's rails user, so storage perms line up without a chown. Host key is pinned via SSH_KNOWN_HOSTS. Docs (DEPLOY.md, AGENTS.md, CLAUDE.md, compose/Caddy comments) updated to match. Co-Authored-By: Claude Opus 4.8 (1M context) <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.
Promotes
devtomain, shipping three commits. Merging this triggers the production deploy (deploy.ymlruns on push tomain→ builds the image → GHCR → SSM → EC2).What ships
c265a3f2Dockerfile(dev+productiontargets), one shared entrypoint, one pre-bootdb:preparelifecycle across dev and prod452d9ce3Dockerfile,docker-compose.prod.yml(app behind Caddy for auto-HTTPS), GitHub Actions → GHCR → SSMa32bdf2bDeploy implications (read before merging)
main— it has never executed against the box before. It will only succeed if theproductionGitHub Environment secrets/variables and the EC2 box are already set up per DEPLOY.md (AWS_ROLE_ARN,EC2_INSTANCE_ID,APP_HOST,SECRET_KEY_BASE,MUDDA_OWNER_*, box + EBS + SSM agent).productionimage stage is behavior-identical to before (same size, same build steps); we only added an explicittarget: productionand refactored dev-only tooling.Verification already done on
devdocker build --target productionand defaultdocker build→ both 491 MB (default == production); dev image → 1.15 GB. All exit 0.uid=1000, boots Rails inproduction; dev image boots indevelopment.make fresh:db:prepare(incl. seeding) runs in an ephemeral container before the app container starts; app then boots healthy (200 on/up).🤖 Generated with Claude Code