slack status without the slack status.zzstoatzz.io/
quickslice

deploy to fly.io + cloudflare pages

- build quickslice from source at v0.17.3 (includes sub claim fix)
- frontend on cloudflare pages, backend on fly.io
- add readme with deployment docs
- clean up old deployment artifacts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+2 -2
.gitignore
··· 1 - # local dev files 2 - site/Caddyfile.dev 1 + # wrangler/cloudflare 2 + .wrangler/ 3 3 4 4 # notes 5 5 oauth-experience.md
+74
Dockerfile
··· 1 + ARG GLEAM_VERSION=v1.13.0 2 + 3 + # Build stage - compile the application 4 + FROM ghcr.io/gleam-lang/gleam:${GLEAM_VERSION}-erlang-alpine AS builder 5 + 6 + # Install build dependencies (including PostgreSQL client for multi-database support) 7 + RUN apk add --no-cache \ 8 + bash \ 9 + git \ 10 + nodejs \ 11 + npm \ 12 + build-base \ 13 + sqlite-dev \ 14 + postgresql-dev 15 + 16 + # Configure git for non-interactive use 17 + ENV GIT_TERMINAL_PROMPT=0 18 + 19 + # Clone quickslice at the v0.17.3 tag (includes sub claim fix) 20 + RUN git clone --depth 1 --branch v0.17.3 https://github.com/bigmoves/quickslice.git /build 21 + 22 + # Install dependencies for all projects 23 + RUN cd /build/client && gleam deps download 24 + RUN cd /build/lexicon_graphql && gleam deps download 25 + RUN cd /build/server && gleam deps download 26 + 27 + # Apply patches to dependencies 28 + RUN cd /build && patch -p1 < patches/mist-websocket-protocol.patch 29 + 30 + # Install JavaScript dependencies for client 31 + RUN cd /build/client && npm install 32 + 33 + # Compile the client code and output to server's static directory 34 + RUN cd /build/client \ 35 + && gleam add --dev lustre_dev_tools \ 36 + && gleam run -m lustre/dev build quickslice_client --minify --outdir=/build/server/priv/static 37 + 38 + # Compile the server code 39 + RUN cd /build/server \ 40 + && gleam export erlang-shipment 41 + 42 + # Runtime stage - slim image with only what's needed to run 43 + FROM ghcr.io/gleam-lang/gleam:${GLEAM_VERSION}-erlang-alpine 44 + 45 + # Install runtime dependencies and dbmate for migrations 46 + ARG TARGETARCH 47 + RUN apk add --no-cache sqlite-libs sqlite libpq curl \ 48 + && DBMATE_ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "arm64" || echo "amd64") \ 49 + && curl -fsSL -o /usr/local/bin/dbmate https://github.com/amacneil/dbmate/releases/latest/download/dbmate-linux-${DBMATE_ARCH} \ 50 + && chmod +x /usr/local/bin/dbmate 51 + 52 + # Copy the compiled server code from the builder stage 53 + COPY --from=builder /build/server/build/erlang-shipment /app 54 + 55 + # Copy database migrations and config 56 + COPY --from=builder /build/server/db /app/db 57 + COPY --from=builder /build/server/.dbmate.yml /app/.dbmate.yml 58 + COPY --from=builder /build/server/docker-entrypoint.sh /app/docker-entrypoint.sh 59 + 60 + # Set up the entrypoint 61 + WORKDIR /app 62 + 63 + # Create the data directory for the SQLite database and Fly.io volume mount 64 + RUN mkdir -p /data && chmod 755 /data 65 + 66 + # Set environment variables 67 + ENV HOST=0.0.0.0 68 + ENV PORT=8080 69 + 70 + # Expose the port the server will run on 71 + EXPOSE $PORT 72 + 73 + # Run the server 74 + CMD ["/app/docker-entrypoint.sh", "run"]
+74
README.md
··· 1 + # quickslice-status 2 + 3 + a status app for bluesky, built with [quickslice](https://github.com/bigmoves/quickslice). 4 + 5 + **live:** https://quickslice-status.pages.dev 6 + 7 + ## architecture 8 + 9 + - **backend**: [quickslice](https://github.com/bigmoves/quickslice) on fly.io - handles oauth, graphql api, jetstream ingestion 10 + - **frontend**: static site on cloudflare pages - vanilla js spa 11 + 12 + ## deployment 13 + 14 + ### backend (fly.io) 15 + 16 + builds quickslice from source at v0.17.3 tag. 17 + 18 + ```bash 19 + fly deploy 20 + ``` 21 + 22 + required secrets: 23 + ```bash 24 + fly secrets set SECRET_KEY_BASE="$(openssl rand -base64 64 | tr -d '\n')" 25 + fly secrets set OAUTH_SIGNING_KEY="$(goat key generate -t p256 | tail -1)" 26 + ``` 27 + 28 + ### frontend (cloudflare pages) 29 + 30 + ```bash 31 + cd site 32 + npx wrangler pages deploy . --project-name=quickslice-status 33 + ``` 34 + 35 + ## oauth client registration 36 + 37 + register an oauth client in the quickslice admin ui at `https://zzstoatzz-quickslice-status.fly.dev/` 38 + 39 + redirect uri: `https://quickslice-status.pages.dev/callback` 40 + 41 + ## lexicon 42 + 43 + uses `io.zzstoatzz.status` lexicon for user statuses. 44 + 45 + ```json 46 + { 47 + "lexicon": 1, 48 + "id": "io.zzstoatzz.status", 49 + "defs": { 50 + "main": { 51 + "type": "record", 52 + "key": "self", 53 + "record": { 54 + "type": "object", 55 + "required": ["status", "createdAt"], 56 + "properties": { 57 + "status": { "type": "string", "maxLength": 128 }, 58 + "createdAt": { "type": "string", "format": "datetime" } 59 + } 60 + } 61 + } 62 + } 63 + } 64 + ``` 65 + 66 + ## local development 67 + 68 + serve the frontend locally: 69 + ```bash 70 + cd site 71 + python -m http.server 8000 72 + ``` 73 + 74 + for oauth to work locally, you'd need to register a separate oauth client with `http://localhost:8000/callback` as the redirect uri and update `CONFIG.clientId` in `app.js`.
-10
docs/Caddyfile
··· 1 - { 2 - admin off 3 - } 4 - 5 - :8000 { 6 - root * /srv 7 - encode gzip 8 - file_server 9 - try_files {path} /index.html 10 - }
-10
docs/Caddyfile.dev
··· 1 - { 2 - admin off 3 - } 4 - 5 - :8000 { 6 - root * . 7 - encode gzip 8 - file_server 9 - try_files {path} /index.html 10 - }
-9
docs/Dockerfile
··· 1 - FROM caddy:2-alpine 2 - 3 - COPY Caddyfile /etc/caddy/Caddyfile 4 - COPY index.html /srv/index.html 5 - COPY app.js /srv/app.js 6 - COPY styles.css /srv/styles.css 7 - COPY favicon.svg /srv/favicon.svg 8 - 9 - EXPOSE 8000
+3
docs/app.js site/app.js
··· 4 4 clientId: 'client_2mP9AwgVHkg1vaSpcWSsKw', 5 5 }; 6 6 7 + // Base path for routing (empty for root domain, '/subpath' for subdirectory) 8 + const BASE_PATH = ''; 9 + 7 10 let client = null; 8 11 let userPreferences = null; 9 12
docs/bufos.json site/bufos.json
docs/favicon.svg site/favicon.svg
-17
docs/fly.toml
··· 1 - app = "quickslice-status" 2 - primary_region = "ewr" 3 - 4 - [build] 5 - dockerfile = "Dockerfile" 6 - 7 - [http_service] 8 - internal_port = 8000 9 - force_https = true 10 - auto_stop_machines = "stop" 11 - auto_start_machines = true 12 - min_machines_running = 0 13 - 14 - [[vm]] 15 - cpu_kind = "shared" 16 - cpus = 1 17 - memory_mb = 256
docs/index.html site/index.html
docs/styles.css site/styles.css
+1 -1
fly.toml
··· 7 7 primary_region = 'ewr' 8 8 9 9 [build] 10 - image = 'ghcr.io/bigmoves/quickslice:latest' 10 + dockerfile = 'Dockerfile' 11 11 12 12 [env] 13 13 DATABASE_URL = 'sqlite:/data/quickslice.db'
lexicons.zip

This is a binary file and will not be displayed.