Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ node_modules/
.wrangler

# env
.env
.env.production
.dev.vars
.dev.vars.*
.env*
.dev.vars*
!.dev.vars.example

# logs
logs/
Expand Down
168 changes: 167 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions services/api/.dev.vars.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GITHUB_APP_CLIENT_ID="your-client-id"
GITHUB_APP_INSTALLATION_ID="your-installation-id"
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
ASSERTION_SIGNING_SECRET="local-shared-secret" # Must match `FOSSBilling/extensions`
EXTENSIONS_REVALIDATE_SECRET="local-revalidate-secret"
EXTENSIONS_V2_MXROUTE_PASSWORD="local-mailbox-password"
2 changes: 1 addition & 1 deletion services/api/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Each service documents its own contract and operational detail in its own `READM

## Configuration & Secrets

- Local secrets go in `.dev.vars` (for example `GITHUB_TOKEN="..."`).
- Local secrets go in `.dev.vars` (for example `GITHUB_APP_PRIVATE_KEY="..."`).
- Bindings, cron, and vars are defined in `wrangler.jsonc` (D1, KV, R2, Durable Objects, rate limiters, service bindings); keep names aligned with `CloudflareBindings`.
- Use Wrangler secrets for production tokens instead of committing them.
- `ASSERTION_SIGNING_SECRET`: HMAC key the extensions v2 API uses to verify short-lived bearer assertions minted by the extensions site (`src/lib/auth/bearer-assertion.ts`). Not sent over the wire — only signs/verifies server-side in each Worker. Add `ASSERTION_SIGNING_SECRET="..."` to `.dev.vars` for local dev; set via `wrangler secret put ASSERTION_SIGNING_SECRET` in production, matching the value configured in the extensions site's Worker.
Expand Down
36 changes: 24 additions & 12 deletions services/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ For local development, put operational values and credentials in the ignored
`.dev.vars` file. Document variable names and placeholders, not production values.
Package storage configuration is listed in [PACKAGING.md](src/services/extensions/v2/PACKAGING.md).

- `GITHUB_TOKEN`: A GitHub Personal Access Token (classic) with public repo read access.
- `GITHUB_APP_CLIENT_ID`: Client ID of the FOSSBilling Integrations GitHub App.
- `GITHUB_APP_INSTALLATION_ID`: Its installation ID in the FOSSBilling organization.
- `GITHUB_APP_PRIVATE_KEY`: GitHub's downloaded App private key (PEM), stored as a secret.
- `ASSERTION_SIGNING_SECRET`: Shared HMAC secret used to verify the short-lived bearer assertions minted by the Extensions site. Configure the same value in both Workers; it is never sent to clients.
- `ASSERTION_SIGNING_SECRET_PREVIOUS`: Optional previous HMAC secret accepted during a signing-key rotation.
- `EXTENSIONS_REVALIDATE_SECRET`: Bearer token the API sends to the Extensions site's `POST /api/revalidate` (over the `EXTENSIONS_FRONTEND` service binding) to purge the site's CDN-cached extension pages after content mutations. Configure the same value in both Workers; it is never sent to clients.
Expand All @@ -111,21 +113,29 @@ Package storage configuration is listed in [PACKAGING.md](src/services/extension
- `M2M_ISSUER`, `M2M_JWKS_URL`, `M2M_AUDIENCE`: Issuer, JWKS endpoint, and expected audience for machine-to-machine access tokens minted by the central auth service (`FOSSBilling/auth`). Defaults live in `wrangler.jsonc`; preview environments need them pointed at the issuing preview. See [extensions v2 README](src/services/extensions/v2/README.md#machine-publishing).

The extension-specific settings apply to extensions v2; Versions, Previews and
Stats also use `GITHUB_TOKEN`. For the assertion format and the rotation procedure, see [its README](src/services/extensions/v2/README.md#authentication).
Stats also use the GitHub App credentials. For the assertion format and the rotation procedure, see [its README](src/services/extensions/v2/README.md#authentication).

### GitHub App authentication

Install **FOSSBilling Integrations** on `FOSSBilling/FOSSBilling` with **Contents**,
**Actions** and **Pull requests** permissions set to **Read-only**. Set the three
`GITHUB_APP_*` runtime values above, using GitHub's downloaded PEM without conversion.
Use separate keys: `api-dev.private-key` for local development,
`api-preview.private-key` for Previews Base, and `api-production.private-key` for
production. All use the same App client and installation IDs.

Octokit manages short-lived installation tokens, restricted to that repository and
those read permissions. Remove the old runtime `GITHUB_TOKEN` after deployment is
verified; GitHub Actions' built-in token remains unchanged.

## Development

### Local Setup

1. Create a `.dev.vars` file for local runtime variables and secrets:
1. Copy `.dev.vars.example` to `.dev.vars` and set your runtime variables and secrets:

```env
GITHUB_TOKEN="your-token"
ASSERTION_SIGNING_SECRET="local-shared-secret"
EXTENSIONS_REVALIDATE_SECRET="local-revalidate-secret"
# Optional while rotating the shared assertion secret.
# ASSERTION_SIGNING_SECRET_PREVIOUS="previous-local-shared-secret"
```
The example contains placeholders only. `cf-typegen` uses it to generate stable
binding types without reading local credentials.

2. Apply migrations to the local D1 databases:

Expand Down Expand Up @@ -209,8 +219,10 @@ Migration jobs serialize access to the shared staging databases.
staging resources; they do not automatically receive separate D1/KV/R2 data.

3. Configure Preview runtime secrets in the API Worker's **Previews Base** settings.
Use a separate assertion-signing secret, revalidation secret and read-only GitHub
token. Match the revalidation secret with the staging frontend. Set M2M issuer,
Use a separate assertion-signing secret, revalidation secret and GitHub App
preview key, alongside `GITHUB_APP_CLIENT_ID` and `GITHUB_APP_INSTALLATION_ID`
(see [GitHub App authentication](#github-app-authentication)). Match the
revalidation secret with the staging frontend. Set M2M issuer,
JWKS and audience variables there when using a separate test issuer. Operational
production variables and secrets remain dashboard-managed with `keep_vars: true`.
4. Configure required checks for the new validation/build job names, then disable the
Expand Down
23 changes: 13 additions & 10 deletions services/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"type": "module",
Comment thread
admdly marked this conversation as resolved.
Resolved
"license": "AGPL-3.0-only",
"scripts": {
"cf-typegen": "wrangler types --env-interface CloudflareBindings",
"cf-deploy": "npm run db:migrate:all:remote && wrangler deploy --minify",
"dev": "wrangler dev",
"cf-typegen": "wrangler types --env-interface CloudflareBindings --env-file .dev.vars.example",
"cf-deploy": "npm run db:migrate:all:remote && cross-env WRANGLER_BUILD_CONDITIONS=workerd,worker,node wrangler deploy --minify",
"dev": "cross-env WRANGLER_BUILD_CONDITIONS=workerd,worker,node wrangler dev",
"db:generate:extensions": "drizzle-kit generate --config drizzle.extensions.config.ts",
"db:generate:central-alerts": "drizzle-kit generate --config drizzle.central-alerts.config.ts",
"db:migrate:extensions-v2:local": "wrangler d1 migrations apply DB_EXTENSIONS --local",
Expand All @@ -21,12 +21,15 @@
"test:coverage": "vitest run --coverage",
"typecheck": "tsc --noEmit",
"test:workers": "vitest run",
"build": "wrangler deploy --dry-run --outdir dist --minify && node scripts/prepare-deployment.ts",
"build": "cross-env WRANGLER_BUILD_CONDITIONS=workerd,worker,node wrangler deploy --dry-run --outdir dist --minify && node scripts/prepare-deployment.ts",
"cf-preview": "wrangler preview --config dist/wrangler.json"
},
"dependencies": {
"@fossbilling/extension-packaging": "*",
"@fossbilling/logging": "*",
"@hono/zod-openapi": "1.6.3",
"@octokit/graphql": "9.0.5",
"@octokit/auth-app": "8.3.1",
"@octokit/core": "7.0.8",
"@octokit/request": "10.0.16",
"@scalar/hono-api-reference": "0.12.18",
"badge-maker": "6.0.0",
Expand All @@ -35,15 +38,15 @@
"hono": "4.13.17",
"semver": "7.8.6",
"uuid": "14.0.3",
"zod": "4.6.7",
"@fossbilling/extension-packaging": "*",
"@fossbilling/logging": "*"
"zod": "4.6.7"
},
"devDependencies": {
"@cloudflare/vitest-plugin": "1.6.1",
"@octokit/graphql": "9.0.5",
"@types/semver": "7.8.0",
"@zip.js/zip.js": "2.24.0",
"cross-env": "10.1.0",
"drizzle-kit": "0.31.11",
"wrangler": "4.158.0",
"@zip.js/zip.js": "2.24.0"
"wrangler": "4.158.0"
}
}
Loading
Loading