{"id":51132697,"url":"https://github.com/stormwarning/respawn-social-web","last_synced_at":"2026-06-25T14:30:34.202Z","repository":{"id":366494462,"uuid":"1275531223","full_name":"stormwarning/respawn-social-web","owner":"stormwarning","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-22T04:01:44.000Z","size":74,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-22T05:21:23.019Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stormwarning.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-20T20:16:36.000Z","updated_at":"2026-06-22T04:01:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stormwarning/respawn-social-web","commit_stats":null,"previous_names":["stormwarning/respawn-social-web"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/stormwarning/respawn-social-web","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormwarning%2Frespawn-social-web","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormwarning%2Frespawn-social-web/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormwarning%2Frespawn-social-web/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormwarning%2Frespawn-social-web/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stormwarning","download_url":"https://codeload.github.com/stormwarning/respawn-social-web/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormwarning%2Frespawn-social-web/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34780124,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-25T14:30:33.432Z","updated_at":"2026-06-25T14:30:34.192Z","avatar_url":"https://github.com/stormwarning.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# respawn-social-web\n\nSvelteKit frontend for a game-focused social app on the **AT Protocol**.\n\n## Architecture\n\nThis app is its own AT Protocol OAuth **confidential client** — it owns auth and\nsessions, and talks **directly to the user's PDS** (server-side) for record\nCRUD. The separate [`respawn-social-api`](https://github.com/stormwarning/respawn-social-api)\nis a pure **API service** (IGDB game data); this app calls it for `/games/*`\nonly and does **not** use its auth endpoints.\n\n- **SSR-first**: sessions restored in `hooks.server.ts` and exposed on\n  `event.locals` (`agent`, `user`), so pages render authed data on the server.\n- **OAuth**: `@atproto/oauth-client-node` (Node runtime). State + session stores\n  are backed by **Netlify Blobs** (Netlify functions are stateless).\n- **Identity / lexicons**: `@atcute/*` for handle/DID resolution and lexicon\n  codegen; `@atproto/api` `Agent` for PDS XRPC.\n- **Hosting**: `adapter-netlify` (Node, `edge: false`).\n\n### Key paths\n\n| Path                                 | Purpose                                            |\n| ------------------------------------ | -------------------------------------------------- |\n| `src/hooks.server.ts`                | Restore OAuth session → `locals.agent` per request |\n| `src/lib/server/oauth/client.ts`     | `NodeOAuthClient` factory + client metadata        |\n| `src/lib/server/oauth/blob-store.ts` | State/session stores on Netlify Blobs              |\n| `src/lib/server/session.ts`          | HMAC-signed session cookie (DID)                   |\n| `src/lib/server/backend.ts`          | Typed fetch wrapper for the backend API            |\n| `src/routes/oauth/*`                 | callback, `client-metadata.json`, `jwks.json`      |\n\n## Setup\n\n\u003e **Node ≥ 22.18** (or ≥ 20.19) required — `oxlint.config.ts` loads via native\n\u003e TS. See `.node-version` (`fnm use`).\n\n```sh\npnpm install\ncp .env.example .env\n```\n\nGenerate the confidential-client signing key and cookie secret:\n\n```sh\nnode scripts/gen-jwk.mjs        # -\u003e paste into PRIVATE_JWK\nopenssl rand -hex 32            # -\u003e paste into COOKIE_SECRET\n```\n\n### Environment\n\n| Var               | Required | Notes                                                                                             |\n| ----------------- | -------- | ------------------------------------------------------------------------------------------------- |\n| `APP_URL`         | prod     | This app's origin. **Unset in dev** → uses the loopback OAuth client (no hosted metadata needed). |\n| `BACKEND_API_URL` | yes      | Base URL of the backend API service.                                                              |\n| `PRIVATE_JWK`     | prod     | ES256 private JWK (single-line JSON) for `private_key_jwt`.                                       |\n| `COOKIE_SECRET`   | yes      | 32+ byte hex, signs the session cookie.                                                           |\n\n## Develop\n\n```sh\npnpm dev          # http://localhost:5173\npnpm check        # svelte-check / types\npnpm lint         # oxlint\npnpm fmt          # oxfmt (write)  | pnpm fmt:check\npnpm test         # vitest (unit)\npnpm test:e2e     # playwright (smoke)\npnpm codegen      # @atcute/lex-cli -\u003e src/lib/lexicons/\n```\n\n\u003e **Dev OAuth note:** the loopback client requires the callback host to be\n\u003e `127.0.0.1`. The dev redirect is `http://127.0.0.1:5173/oauth/callback`, so\n\u003e start the login flow from `http://127.0.0.1:5173` (not `localhost`).\n\n## Deploy (Netlify)\n\nSet `APP_URL`, `BACKEND_API_URL`, `PRIVATE_JWK`, `COOKIE_SECRET` in the site\nenv. `adapter-netlify` generates the function + redirects; Netlify Blobs is\nenabled automatically. Build: `pnpm build`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstormwarning%2Frespawn-social-web","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstormwarning%2Frespawn-social-web","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstormwarning%2Frespawn-social-web/lists"}