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

expand migration notes with UI section and deployment checklist

Changed files
+82 -6
notes
+82 -6
notes/quickslice-migration.md
··· 53 53 <script src="https://cdn.jsdelivr.net/gh/bigmoves/quickslice@v0.17.3/quickslice-client-js/dist/quickslice-client.min.js"></script> 54 54 ``` 55 55 56 - ### 3. OAuth flow 56 + ### 3. the UI 57 57 58 - quickslice handles the OAuth server side. the frontend just needs to: 58 + since quickslice serves its own admin UI at the root path, we host our frontend separately on cloudflare pages. the frontend is vanilla JS - no framework, just a single `app.js` file. 59 + 60 + **OAuth with quickslice-client-js** 59 61 60 - 1. create a client with `QuicksliceClient.create()` 61 - 2. call `client.signIn()` to start the flow 62 - 3. handle the callback (quickslice redirects back with auth tokens) 63 - 4. use `client.agent` for authenticated AT protocol operations 62 + the `quickslice-client-js` library handles the OAuth flow in the browser: 63 + 64 + ```javascript 65 + const client = await QuicksliceClient.create({ 66 + server: 'https://zzstoatzz-quickslice-status.fly.dev', 67 + clientId: 'client_2mP9AwgVHkg1vaSpcWSsKw', 68 + redirectUri: window.location.origin + '/', 69 + }); 70 + 71 + // start login 72 + await client.signIn(handle); 73 + 74 + // after redirect, client.agent is authenticated 75 + const { data } = await client.agent.getProfile({ actor: client.agent.session.did }); 76 + ``` 64 77 65 78 the redirect URI is just the root of your site (e.g., `https://status.zzstoatzz.io/`). 66 79 80 + **GraphQL queries** 81 + 82 + quickslice auto-generates a GraphQL API from your lexicons. querying status records looks like: 83 + 84 + ```javascript 85 + const response = await fetch(`${CONFIG.server}/api/graphql`, { 86 + method: 'POST', 87 + headers: { 'Content-Type': 'application/json' }, 88 + body: JSON.stringify({ 89 + query: ` 90 + query GetStatuses($did: String!) { 91 + ioZzstoatzzStatusRecords( 92 + where: { did: { eq: $did } } 93 + orderBy: { createdAt: DESC } 94 + first: 50 95 + ) { 96 + nodes { uri did emoji text createdAt } 97 + } 98 + } 99 + `, 100 + variables: { did } 101 + }) 102 + }); 103 + ``` 104 + 105 + no need to write resolvers or schema - it's all generated from the lexicon definitions. 106 + 67 107 ## problems we hit 68 108 69 109 ### the `sub` claim fix ··· 134 174 │ AT Protocol │ 135 175 │ (bluesky PDS, jetstream firehose) │ 136 176 └─────────────────────────────────────────────────────────┘ 177 + ``` 178 + 179 + ## what quickslice eliminated 180 + 181 + the rust backend was ~2000 lines of code handling: 182 + 183 + - OAuth server implementation (PKCE + DPoP) 184 + - jetstream consumer for firehose ingestion 185 + - custom API endpoints for reading/writing statuses 186 + - session management 187 + - database queries 188 + 189 + with quickslice, all of that is replaced by: 190 + 191 + - a Dockerfile that builds quickslice from source 192 + - a fly.toml with env vars 193 + - two secrets 194 + 195 + the frontend is still custom (~1200 lines), but the backend complexity is gone. 196 + 197 + ## deployment checklist 198 + 199 + when deploying quickslice: 200 + 201 + ```bash 202 + # 1. set required secrets 203 + fly secrets set SECRET_KEY_BASE="$(openssl rand -base64 64 | tr -d '\n')" 204 + fly secrets set OAUTH_SIGNING_KEY="$(goat key generate -t p256 | tail -1)" 205 + 206 + # 2. deploy (builds from source, takes ~3 min) 207 + fly deploy 208 + 209 + # 3. in quickslice admin UI: 210 + # - set domain authority (e.g., io.zzstoatzz) 211 + # - add supported lexicons 212 + # - register OAuth client with redirect URI 137 213 ``` 138 214 139 215 ## key takeaways