Skip to content

Commit 861423a

Browse files
authored
Merge pull request #145 from eurosky-social/eurosky/dev
dev branch with our own appview
2 parents 296c338 + c8e1b4a commit 861423a

6 files changed

Lines changed: 801 additions & 7 deletions

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Eurosky fork: build the web app and deploy it to the DEV Bunny.net zone.
2+
#
3+
# Dev mirror of deploy-web-staging-bunny.yml, served at https://dev.mu.social.
4+
# Unlike staging (which continuously tracks eurosky/fork), dev is manual-only:
5+
# run the workflow from the Actions tab and pick any branch to put it live.
6+
# Useful for trying out risky or experimental branches without disturbing
7+
# staging. New file (not in upstream) -> no merge surface.
8+
#
9+
# Dev differs from staging only in build-time env (see the Build step): its
10+
# own OAuth client identity (assertions come from the shared non-prod worker
11+
# at oauth-staging.mu.social - see services/oauth/bunny/staging.ts), and its
12+
# own throwaway Plausible domain. Search noindexing is handled at the Bunny
13+
# pull zone (Edge Rule adds X-Robots-Tag: noindex, nofollow), not in the
14+
# build.
15+
#
16+
# Required repo secrets (Settings -> Secrets and variables -> Actions):
17+
# DEV_BUNNY_STORAGE_ZONE full base URL incl. zone, e.g.
18+
# https://storage.bunnycdn.com/mu-dev
19+
# DEV_BUNNY_STORAGE_PASSWORD storage zone password (AccessKey for uploads)
20+
# DEV_BUNNY_PULLZONE_ID numeric dev pull zone id (cache purge)
21+
# BUNNY_API_KEY account-level API key (shared with prod)
22+
name: Deploy web dev (Bunny)
23+
24+
on:
25+
# Manual only: Actions tab -> "Deploy web dev (Bunny)" -> Run workflow ->
26+
# pick the branch to deploy. No push trigger; dev is an on-demand target.
27+
workflow_dispatch: {}
28+
29+
# Never run two dev deploys at once; a newer run supersedes an in-flight one.
30+
# Separate group from prod/staging so the three never queue on each other.
31+
concurrency:
32+
group: deploy-web-dev-bunny
33+
cancel-in-progress: true
34+
35+
# Least privilege: only checks out + uploads to Bunny via Bunny secrets; never
36+
# uses GITHUB_TOKEN to write to the repo.
37+
permissions:
38+
contents: read
39+
40+
jobs:
41+
deploy:
42+
runs-on: ubuntu-latest
43+
steps:
44+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
45+
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
46+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
47+
with:
48+
node-version-file: package.json
49+
cache: pnpm
50+
# bunny_build.sh does: frozen install (CI) -> intl:build -> build-web ->
51+
# static-path rewrite -> _redirects. CI=true is set by Actions, so it
52+
# uses --frozen-lockfile.
53+
- name: Build web
54+
run: bash ./scripts/bunny_build.sh
55+
env:
56+
# Dev is its own atproto OAuth client: client_id derives from this
57+
# base URL (gen-oauth-metadata.js emits the metadata into the build,
58+
# so dev self-publishes it at /oauth-client-metadata.json).
59+
# Client assertions come from the shared non-prod worker
60+
# (services/oauth/bunny/staging.ts): one instance serves EVERY
61+
# *.mu.social environment by deriving the client_id from the request
62+
# Origin - adding version N needs no OAuth change at all. Prod stays
63+
# on its own pinned worker at oauth.mu.social.
64+
EXPO_PUBLIC_OAUTH_BASE_URL: https://dev.mu.social
65+
EXPO_PUBLIC_OAUTH_ASSERTION_URL: https://oauth-staging.mu.social/client-assertion
66+
# Dev routes AUTHED appview traffic to the Eurosky appview: the PDS
67+
# resolves this DID's #bsky_appview service endpoint
68+
# (https://api.eurosky.network) and proxies there via the
69+
# atproto-proxy header. UNAUTHED traffic (logged-out browsing,
70+
# brand.json services.publicApi/appView) stays on Bluesky's public
71+
# API for now: the Eurosky appview does not resolve handles yet
72+
# (handle lookups 404, profiles return handle.invalid), which would
73+
# break logged-out views.
74+
EXPO_PUBLIC_BLUESKY_PROXY_DID: did:web:api.eurosky.network
75+
# Geolocation: same first-party endpoint as prod (services/geolocation/
76+
# serves any origin), drives the regional moderation labelers.
77+
EXPO_PUBLIC_ENABLE_GEOLOCATION: 'true'
78+
EXPO_PUBLIC_GEOLOCATION_URL: https://ip.mu.social
79+
EXPO_PUBLIC_ENABLE_LIVE_EVENTS: 'false'
80+
EXPO_PUBLIC_ENABLE_APP_CONFIG: 'false'
81+
# Product-analytics events otherwise POST to events.bsky.app. Disabled;
82+
# GrowthBook flag fetching is unaffected (separate from this client).
83+
EXPO_PUBLIC_ENABLE_METRICS: 'false'
84+
# Deliberately NOT the prod Plausible site: dev.mu.social is
85+
# unregistered in Plausible, so events are accepted and discarded and
86+
# dev traffic never pollutes prod stats. (Blanking the value is not
87+
# an option: bunny_build.sh's `:=` re-defaults empty to mu.social.)
88+
EXPO_PUBLIC_PLAUSIBLE_DOMAIN: dev.mu.social
89+
# First-party Bunny proxy for Plausible (see services/plausible/). Needs
90+
# the scheme: the tracker posts to ${API_HOST}/api/event.
91+
EXPO_PUBLIC_PLAUSIBLE_API_HOST: https://events.mu.social
92+
# Shares the prod live-sports proxy (see services/footballData/); the
93+
# endpoint is read-only and cached, so dev can reuse it.
94+
EXPO_PUBLIC_FOOTBALLDATA_API_URL: https://sports.mu.social/v4
95+
- name: Upload to Bunny + purge cache
96+
run: bash ./scripts/bunny_upload.sh
97+
env:
98+
BUNNY_STORAGE_ZONE: ${{ secrets.DEV_BUNNY_STORAGE_ZONE }}
99+
BUNNY_STORAGE_PASSWORD: ${{ secrets.DEV_BUNNY_STORAGE_PASSWORD }}
100+
BUNNY_API_KEY: ${{ secrets.BUNNY_API_KEY }}
101+
BUNNY_PULLZONE_ID: ${{ secrets.DEV_BUNNY_PULLZONE_ID }}

.github/workflows/deploy-web-staging-bunny.yml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
# New file (not in upstream) -> no merge surface.
77
#
88
# Staging differs from prod only in build-time env (see the Build step):
9-
# its own OAuth client identity + assertion worker, and a throwaway Plausible
10-
# domain. Search noindexing is handled at the Bunny pull zone (Edge Rule adds
9+
# its own OAuth client identity (assertions come from the shared non-prod
10+
# worker at oauth-staging.mu.social, which accepts any *.mu.social subdomain -
11+
# see services/oauth/bunny/staging.ts), and a throwaway Plausible domain.
12+
# Search noindexing is handled at the Bunny pull zone (Edge Rule adds
1113
# X-Robots-Tag: noindex, nofollow), not in the build.
1214
#
1315
# Required repo secrets (Settings -> Secrets and variables -> Actions):
@@ -54,10 +56,11 @@ jobs:
5456
env:
5557
# Staging is its own atproto OAuth client: client_id derives from this
5658
# base URL (gen-oauth-metadata.js emits the metadata into the build,
57-
# so staging self-publishes it at /oauth-client-metadata.json), and
58-
# client assertions are signed by the staging worker instance - the
59-
# prod worker pins prod's CLIENT_ID/ALLOWED_ORIGIN and would reject
60-
# staging's requests. Same services/oauth/ script, second deployment.
59+
# so staging self-publishes it at /oauth-client-metadata.json).
60+
# Client assertions come from the shared non-prod worker
61+
# (services/oauth/bunny/staging.ts): one instance serves EVERY
62+
# *.mu.social environment by deriving the client_id from the request
63+
# Origin. Prod stays on its own pinned worker at oauth.mu.social.
6164
EXPO_PUBLIC_OAUTH_BASE_URL: https://staging.mu.social
6265
EXPO_PUBLIC_OAUTH_ASSERTION_URL: https://oauth-staging.mu.social/client-assertion
6366
# Geolocation: same first-party endpoint as prod (services/geolocation/

services/oauth/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,26 @@ It is **stateless**: no DB, no KV, no token storage. The only state is one
1414
secret - the client private key. User tokens and DPoP keys stay in the
1515
browser (IndexedDB).
1616

17+
## Two scripts: prod (pinned) vs staging/dev (wildcard subdomain)
18+
19+
- `bunny/index.ts` - **prod**. Pins one exact `CLIENT_ID` + `ALLOWED_ORIGIN`
20+
pair via env. Deployed at `oauth.mu.social`, serves only `mu.social`.
21+
- `bunny/staging.ts` - **every non-prod environment**. Accepts any https
22+
SUBDOMAIN of `ALLOWED_PARENT_DOMAIN` (e.g. `*.mu.social`) and derives the
23+
client_id from the validated request Origin as
24+
`<origin>/oauth-client-metadata.json` - the exact URL each environment
25+
self-publishes its metadata at. Deployed at `oauth-staging.mu.social`.
26+
The apex is deliberately rejected, so this instance can never mint under
27+
prod's identity. Spinning up `v10.mu.social` needs NO OAuth change: point
28+
its build's `EXPO_PUBLIC_OAUTH_ASSERTION_URL` at the staging worker, done.
29+
30+
Both are the same strict reconstructing minter and share the same keypair
31+
(the committed public JWKS is inlined into every build's metadata). The
32+
security model is identical too: the Origin check is defense-in-depth, not
33+
authentication, in both variants (any non-browser caller sets Origin freely);
34+
PKCE + pinned redirect_uri + DPoP do the real work. The split just keeps
35+
prod's config maximally rigid while making new dev versions free.
36+
1737
## What it does
1838

1939
`POST` `{ header, payload }` (produced by `@atproto/oauth-client` in the app)
@@ -46,6 +66,14 @@ that whole class of clock-skew failures.
4666
`https://<domain>/oauth-client-metadata.json`
4767
- `ALLOWED_ORIGIN` - exact browser Origin, e.g. `https://<domain>`
4868

69+
For the staging/dev worker, deploy `bunny/staging.ts` the same way (hostname
70+
e.g. `oauth-staging.<your-domain>`) with:
71+
72+
- `OAUTH_PRIVATE_JWK` (**Environment SECRET**) - same secret as prod
73+
- `ALLOWED_PARENT_DOMAIN` - apex whose https subdomains are accepted,
74+
e.g. `mu.social` (no `CLIENT_ID`/`ALLOWED_ORIGIN` - both derive from
75+
the request Origin)
76+
4977
## Wire the app to it
5078

5179
The app calls `OAUTH_ASSERTION_URL` (see `../src/config/oauth.ts`). Default:
@@ -79,3 +107,13 @@ WORKER_URL=http://localhost:8000 node test.mjs
79107

80108
`CLIENT_ID` / `ALLOWED_ORIGIN` default to the `mu.social` config and can be
81109
overridden via env to match the target instance.
110+
111+
`test-staging.mjs` is the equivalent suite for `bunny/staging.ts`: the same
112+
minter guardrails plus the wildcard origin gate (subdomains accepted with
113+
per-origin derived identities; apex, lookalike domains, http, explicit ports,
114+
and cross-environment identities rejected). `PARENT_DOMAIN` defaults to
115+
`mu.social`.
116+
117+
```bash
118+
WORKER_URL=https://oauth-staging.mu.social node test-staging.mjs
119+
```

0 commit comments

Comments
 (0)