monorepo wit docs

This commit is contained in:
matamune 2026-05-16 00:33:10 +00:00
parent 88ddcfba39
commit e3e4d1823d
Signed by: matamune
GPG key ID: 3BB8E7D3B968A324
43 changed files with 1585 additions and 29 deletions

View file

@ -0,0 +1,21 @@
---
title: Architecture
description: How feeds, targets, state, and flow dispatch fit together.
---
# Architecture
Patch has three runtime pieces:
- The HTTP server exposes health and admin flow endpoints.
- The feed poller reads configured upstream feeds on an interval.
- The JSONL store keeps feed signals, fork-sync jobs, flow events, and dispatch
outcomes under `DATA_DIR`.
The poller does not know how to complete product-specific work. It only turns a
feed entry into a normalized signal and follows the target configured for that
source.
Flow targets create a generic `FlowEvent` and use the shared flow client. That
client gives Patch the same contract in local development and in HTTP-backed
service mode.

View file

@ -0,0 +1,17 @@
---
title: Flow boundary
description: What Patch owns and what codex-flow packages own.
---
# Flow boundary
Patch owns upstream observation. It knows about feeds, source ids, feed state,
and dispatch attempts.
codex-flow owns execution. Flow packages match `FlowEvent.type` and payload
schema, run Bun or gated Code Mode steps, and emit `FLOW_RESULT`.
Product completion stays outside Patch. For example, OpenAI Codex release
automation should decide how to update a fork, open a branch, ask for review, or
publish a result. Patch only dispatches the upstream release event and records
the dispatch outcome.

View file

@ -0,0 +1,260 @@
---
title: Forgejo forking problem space
description: Historical analysis for tracking and possibly forking Forgejo from Patch signals.
---
# Forgejo Forking Problem Space
Date: 2026-05-12
## Current Setup
Created a jojo.build mirror:
- Repo: https://jojo.build/peezy-tech/jojo
- SSH: `ssh://git@jojo.build/peezy-tech/jojo.git`
- Upstream: `https://codeberg.org/forgejo/forgejo.git`
- Default branch: `forgejo`
- Mirror interval: `8h`
This is currently a mirror, not a working fork branch. That is a good starting
point for source inspection and tracking upstream movement. It is not yet a good
place for custom patches, because mirror updates are expected to follow upstream.
## Executive Summary
Forking Forgejo is feasible, but it is not a small maintenance commitment.
Forgejo is a full Go web application with a substantial frontend build,
database migrations, templates, Actions integration, SSH/HTTP Git serving,
packages, OAuth2, and admin surfaces. A fork is reasonable only if the desired
customization cannot be achieved through supported configuration, OIDC, reverse
proxy auth, static assets, or template overrides.
For SIWE specifically, the preferred order remains:
1. Use SIWE through an external OpenID Connect provider.
2. Use a trusted reverse proxy that performs SIWE and passes identity headers.
3. Fork Forgejo only if we need first-class SIWE UX or native account semantics.
## Upstream Release Cadence
Forgejo stable releases are published quarterly. LTS releases are published in
the first quarter of each year and receive critical bugfix/security support for
one year and three months. The current LTS line is v15.0, released 2026-04-16
and supported until 2027-07-15.
Implication: a fork has to either:
- Track every quarterly release and absorb breaking changes often.
- Track only LTS releases and accept slower access to new Forgejo features.
- Track upstream `forgejo` continuously, which maximizes merge churn.
For a production instance, an LTS-based fork is the most conservative option.
## What A Fork Would Own
At minimum, a maintained fork owns:
- A source branch policy.
- CI for Go, frontend, templates, linting, and tests.
- Binary or container image builds.
- A release/version naming scheme.
- Security patch intake.
- Upgrade rehearsals against jojo.build data.
- Documentation for local patches.
- A rollback path to upstream Forgejo.
The risky areas are not only code conflicts. They are also database migrations,
template changes, auth/session behavior, Actions behavior, and generated assets.
## Build And Test Surface
Forgejo source includes:
- `go.mod` for the backend.
- `package.json` and `webpack.config.js` for frontend assets.
- `Makefile` for build/test targets.
- `Dockerfile` and `Dockerfile.rootless`.
- Published release notes in `release-notes-published/`.
Official build docs require Go, Node.js/npm, and `make`. Older docs mention Go
1.22+ and Node 20+, but we should verify exact versions from the branch we pin
before building production artifacts.
Initial local validation target should be:
```bash
make help
make deps-frontend
make frontend
make test
make build
```
Exact target names need confirmation from the current Makefile before scripting.
## Auth Extension Surface
Forgejo has configurable auth sources:
- Local database auth.
- LDAP via BindDN.
- LDAP simple auth.
- SMTP.
- PAM.
- OAuth2.
- OpenID Connect.
- OpenID.
- Reverse proxy header auth.
From source inspection, auth source types are compiled into the application.
OAuth2 providers are registered in Go during init via an internal Goth provider
registry. That registry is useful for patching Forgejo, but it is not an
external plugin API.
Native SIWE would likely require one of these approaches:
- Add a first-class OAuth2/OIDC provider if SIWE is presented as OIDC.
- Add a custom auth flow and routes for EIP-4361 challenge/signature handling.
- Add account-linking fields for wallet addresses and ENS-derived display data.
- Add admin settings and migration(s) if wallet addresses become persisted
first-class identities.
The more native the SIWE behavior, the more the fork touches security-critical
surface: sessions, CSRF, account linking, auto-registration, 2FA bypass rules,
and recovery flows.
## SIWE Fork Design Questions
Before writing code, resolve these:
- Is an Ethereum address the canonical Forgejo username, an external account
identifier, or merely a linked credential?
- Do users need email addresses?
- Can a wallet create a new Forgejo account automatically?
- How are lost wallets handled?
- Can one Forgejo account link multiple wallets?
- Is ENS only display metadata, or does it influence identity?
- Are smart-contract wallets/EIP-1271 required?
- Which chains are accepted?
- Does SIWE bypass local 2FA, complement it, or become a second factor?
- How do Git over HTTPS, SSH keys, access tokens, and API auth map to wallet
sign-in?
These questions matter more than the login button. Forgejo identity is used by
Git, issues, packages, Actions, API tokens, and admin permissions.
## Fork Strategies
### Strategy A: No Fork
Use config, OIDC, reverse proxy auth, and supported UI customization.
Best for:
- Branding.
- Trusted SIWE/OIDC experiment.
- Avoiding security patch ownership.
Risk:
- Less native UX.
- Identity model constrained by Forgejo's existing auth flows.
### Strategy B: Patch Branch On LTS
Create a branch like `jojo/v15.0` from the current LTS tag, maintain a minimal
patch stack, and periodically rebase or merge v15.0 patch releases.
Best for:
- Production stability.
- Small, well-contained changes.
- Predictable security patch intake.
Risk:
- Larger jumps when moving to the next LTS.
- Backport work if desired features land in newer stable releases.
### Strategy C: Patch Branch On Upstream `forgejo`
Maintain `jojo/forgejo` on top of upstream development.
Best for:
- Contributing changes upstream.
- Early access to Forgejo improvements.
Risk:
- Highest churn.
- Bad fit for production unless we have strong CI and staging.
### Strategy D: Hard Product Fork
Rename/rebrand deeply, ship independent releases, and diverge freely.
Best for:
- Building a distinct forge product.
Risk:
- Largest maintenance burden.
- Security, release, migration, and support responsibilities become ours.
This should be avoided unless jojo.build becomes a product-level Forgejo
distribution.
## Recommended Path
Use the mirror as the upstream intake point. Do not customize the mirror branch.
Next create a working fork branch only when we have a concrete patch to test:
- `jojo/v15.0` from the latest v15.0.x tag for production-minded patches.
- `jojo/forgejo` from upstream `forgejo` for exploratory upstreamable patches.
For SIWE, first prototype without a Forgejo fork:
1. Configure a SIWE OIDC provider against a test Forgejo instance.
2. Test user creation, account linking, logout, API tokens, Git over HTTPS, and
SSH key management.
3. Separately test reverse proxy header auth with a toy trusted auth service.
4. Only fork if those approaches fail on essential UX or identity semantics.
## Maintenance Checklist For Any Fork
- Track upstream release notes and security announcements.
- Keep a patch inventory with rationale and owner.
- Rebase/merge upstream into a staging branch first.
- Run full build and test suite.
- Run upgrade rehearsal against a copy of production data.
- Smoke test login, repo browsing, Git SSH, Git HTTPS, Actions, packages,
mirrors, and admin pages.
- Keep a rollback artifact for the previous known-good binary/container.
- Document every app.ini setting required by the fork.
## Open Questions
- Is jojo.build currently installed from binary, container, package manager, or
local build?
- How is production deployment currently rolled out and rolled back?
- Do we want custom patches to be private indefinitely, or upstreamable?
- Would SIWE be required for all users, optional, or only for selected orgs?
- Is the target experience "login with wallet" or broader onchain identity?
- Are we willing to run an external IdP such as a SIWE OIDC provider?
## Sources Consulted
- Forgejo mirror created from `https://codeberg.org/forgejo/forgejo.git`.
- Forgejo auth docs: https://forgejo.org/docs/v11.0/user/authentication/
- Forgejo OAuth2 provider docs: https://forgejo.org/docs/v13.0/user/oauth2-provider/
- Forgejo customization docs: https://forgejo.org/docs/next/admin/advanced/customization/
- Forgejo config cheat sheet: https://forgejo.org/docs/latest/admin/config-cheat-sheet/
- Forgejo upgrade guide: https://forgejo.org/docs/next/admin/upgrade/
- Forgejo release schedule: https://forgejo.org/docs/v11.0/admin/release-schedule
- Forgejo compile-from-source docs: https://forgejo.org/docs/v7.0/developer/from-source/
- SIWE docs: https://docs.siwe.xyz/
- SIWE hosted OIDC docs: https://docs.login.xyz/servers/oidc-provider/hosted-oidc-provider

View file

@ -0,0 +1,25 @@
---
title: Upstream use cases
description: How Patch is used for Forgejo and Codex upstream tracking.
---
# Upstream use cases
## Forgejo
Patch watches Codeberg Forgejo branch and release feeds. Branch activity can be
notification-only. Release activity can still produce a legacy `fork_sync` job
for a jojo-hosted Forgejo fork workflow.
The Forgejo fork remains a maintenance decision outside Patch. Patch records the
upstream signal and job; fork policy, branch policy, CI, and release ownership
belong to the Forgejo fork process.
## OpenAI Codex
Patch watches GitHub OpenAI Codex branch and release feeds. Branch activity can
notify operators. Release activity emits a generic `upstream.release` flow event
for codex-flow automation.
This keeps the release feed integration stable while the actual Codex release
automation evolves in flow packages and backends.

View file

@ -0,0 +1,55 @@
---
title: Configure feed sources
description: Add upstream feeds and choose notification, fork-sync, or flow targets.
---
# Configure feed sources
Feed sources live in a JSON file referenced by `FEED_SOURCES_PATH`. The bundled
file is `apps/patch/feed-sources.json`.
## Choose an event
Use `event: "push"` for branch commit feeds and `event: "release"` for release
feeds. Patch normalizes both into `FeedSignal` records.
## Choose a target
`notify_only` stores the signal and can notify Discord:
```json
{
"provider": "github",
"repoFullName": "peezy-tech/codex",
"branch": "main",
"mode": "notify_only"
}
```
`fork_sync` stores a legacy fork-sync job for release entries:
```json
{
"provider": "jojo",
"repoFullName": "peezy-tech/jojo",
"branch": "forgejo",
"mode": "fork_sync"
}
```
`flow_dispatch` creates a generic `FlowEvent` and dispatches it:
```json
{
"mode": "flow_dispatch",
"eventType": "upstream.release",
"dispatchUrlEnv": "PATCH_FLOW_DISPATCH_URL",
"dispatchSecretEnv": "PATCH_FLOW_DISPATCH_SECRET",
"payload": {
"repo": "openai/codex"
}
}
```
Set `primeOnly: false` only when old feed entries should be emitted on the first
poll.

View file

@ -0,0 +1,41 @@
---
title: Dispatch and replay flow events
description: Use local or HTTP flow execution and retry stored events.
---
# Dispatch and replay flow events
Patch creates deterministic event ids:
```text
patch:<sourceId>:<entryId>:<eventType>
```
Dispatch is idempotent at the flow backend by event id. Replay intentionally
asks the backend to create another attempt for the stored event.
## Select HTTP dispatch
```bash
PATCH_FLOW_DISPATCH_URL=http://127.0.0.1:7345/events
PATCH_FLOW_DISPATCH_SECRET=dev-secret
```
Patch signs HTTP dispatches with the shared flow HMAC header when a secret is
configured.
## Use local dispatch
Leave `PATCH_FLOW_DISPATCH_URL` unset. Patch creates a local flow client rooted
at the app working directory and runs matching flows synchronously.
## Retry or replay
```bash
curl -X POST http://127.0.0.1:3000/flow-events/<event-id>/retry
curl -X POST http://127.0.0.1:3000/flow-events/<event-id>/replay
```
`retry` dispatches the stored event again. `replay` calls the backend replay
endpoint when HTTP mode is configured, or dispatches locally when no backend URL
is configured.

View file

@ -0,0 +1,20 @@
---
title: Enable Discord output
description: Send selected upstream signals to a Discord webhook.
---
# Enable Discord output
Discord notifications are disabled by default.
```bash
DISCORD_OUTPUT_ENABLED=true
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
DISCORD_NOTIFY_EVENTS=push,release
```
`DISCORD_NOTIFY_EVENTS` is a comma-separated allow list. Patch still stores feed
signals when Discord is disabled or when an event is not in the allow list.
Notifications include provider, repository, event type, branch, author, short
SHA, queued job kind, and source id when those fields are available.

View file

@ -0,0 +1,35 @@
---
title: Run Patch locally
description: Install dependencies, start the service, and run checks.
---
# Run Patch locally
Install from the repository root:
```bash
bun install
```
Start the service app:
```bash
bun run dev
```
Root scripts delegate into the workspace:
```bash
bun run check
bun run test
bun run docs:dev
```
Use explicit paths when you run the app package directly:
```bash
cd apps/patch
DATA_DIR=./data FEED_SOURCES_PATH=./feed-sources.json bun run dev
```
`GET /healthz` returns `ok` when the server is running.

48
docs/pages/index.md Normal file
View file

@ -0,0 +1,48 @@
---
title: patch.moi
description: Feed watching and flow dispatch for upstream project events.
---
# patch.moi
Patch is a small Bun service that watches upstream Atom/RSS feeds and turns
selected upstream activity into durable Patch records, Discord notifications,
or generic codex-flow events.
The service keeps the product boundary narrow:
- Feeds describe upstream repositories and events.
- Patch normalizes feed entries into `FeedSignal` records.
- Targets decide whether a signal is notification-only, a legacy fork-sync job,
or a generic flow dispatch.
- Flow dispatch uses `@peezy.tech/flow-runtime/client` so Patch can run flows
locally during development or send events to an HTTP backend in service mode.
- Admin endpoints inspect stored flow events and dispatch records, then retry or
replay events when an upstream automation needs another attempt.
```mermaid
flowchart LR
Feed["Atom or RSS feed"] --> Patch["Patch poller"]
Patch --> Signal["FeedSignal JSONL"]
Signal --> Notify["Discord output"]
Signal --> Job["fork_sync job JSONL"]
Signal --> Event["FlowEvent JSONL"]
Event --> Client["flow-runtime client"]
Client --> Local["local flow execution"]
Client --> Backend["HTTP flow backend"]
```
## Start here
- New service setup: [Watch an upstream release](tutorials/watch-upstream-release).
- Codex release automation: [Dispatch a Codex release flow](tutorials/dispatch-codex-release-flow).
- Running the service: [Run Patch locally](guides/run-patch-locally).
- Exact feed shape: [Feed sources](reference/feed-sources).
- Admin operations: [HTTP API](reference/http-api).
## What is in this repo
- `apps/patch`: the Patch Bun service, feed poller, JSONL store, Discord output,
and flow dispatch adapter.
- `docs`: this Tome documentation site, organized with the Diataxis framework.
- `Dockerfile`: container image for the Patch service app.

View file

@ -0,0 +1,25 @@
---
title: Environment
description: Runtime environment variables used by Patch.
---
# Environment
| Variable | Default | Purpose |
| --- | --- | --- |
| `HOST` | `0.0.0.0` | Server bind host. |
| `PORT` | `3000` | Server port. |
| `DATA_DIR` | `./data` | Directory for JSONL state files. |
| `FEED_SOURCES_PATH` | unset | Enables feed polling from the configured JSON file. |
| `PATCH_ADMIN_TOKEN` | unset | Protects admin flow endpoints when set. |
| `PATCH_FLOW_DISPATCH_URL` | unset | Default flow backend URL for dispatch targets. |
| `PATCH_FLOW_BACKEND_URL` | unset | Alternate default backend base URL. |
| `PATCH_FLOW_DISPATCH_SECRET` | unset | HMAC secret for HTTP flow dispatch. |
| `DISCORD_OUTPUT_ENABLED` | `false` | Enables Discord webhook output. |
| `DISCORD_WEBHOOK_URL` | unset | Discord webhook target. |
| `DISCORD_NOTIFY_EVENTS` | `push,release` | Comma-separated notification allow list. |
| `CODEX_APP_SERVER_CODEX_COMMAND` | unset | Passed to local code-mode flow execution. |
| `CODEX_HOME` | unset | Passed to local code-mode flow execution. |
Feed target fields can override backend settings with `dispatchUrl`,
`dispatchUrlEnv`, and `dispatchSecretEnv`.

View file

@ -0,0 +1,44 @@
---
title: Feed sources
description: JSON schema by convention for upstream feed configuration.
---
# Feed sources
`FEED_SOURCES_PATH` points at a JSON object with a `sources` array.
```ts
type FeedSourceConfig = {
id: string;
provider: "codeberg" | "github" | "jojo";
url: string;
event: "push" | "release";
repo: {
owner: string;
name: string;
fullName: string;
webUrl: string;
defaultBranch?: string;
};
target?: FeedForkSyncTarget | FeedFlowDispatchTarget;
pollIntervalSeconds?: number;
primeOnly?: boolean;
};
```
## Flow dispatch target
```ts
type FeedFlowDispatchTarget = {
mode: "flow_dispatch";
eventType: string;
dispatchUrl?: string;
dispatchUrlEnv?: string;
dispatchSecretEnv?: string;
payload?: Record<string, unknown>;
};
```
The flow payload includes provider, event, source id, entry id, title, URL,
author, published time, repository fields, ref, SHA, tag, and raw feed metadata.
Values from `target.payload` are merged last.

View file

@ -0,0 +1,41 @@
---
title: HTTP API
description: Health, flow event inspection, retry, replay, and dispatch history.
---
# HTTP API
## Health
```text
GET /healthz
```
Returns `ok`.
## Flow events
```text
GET /flow-events?type=<type>&limit=<n>
GET /flow-events/:id?limit=<n>
POST /flow-events/:id/retry
POST /flow-events/:id/replay
```
The list endpoint returns stored events newest first. The detail endpoint
returns the event and matching dispatch records.
## Dispatches
```text
GET /flow-dispatches?eventId=<id>&status=dispatched|failed|skipped&limit=<n>
```
## Admin auth
When `PATCH_ADMIN_TOKEN` is set, flow endpoints require one of:
```text
Authorization: Bearer <token>
X-Patch-Admin-Token: <token>
```

View file

@ -0,0 +1,19 @@
---
title: JSONL state
description: Files written under DATA_DIR.
---
# JSONL state
Patch uses append-only JSONL files for durable service records.
| File | Contents |
| --- | --- |
| `feed-state.json` | Per-source last seen entry and last checked timestamp. |
| `feed-events.jsonl` | Normalized `FeedSignal` records. |
| `feed-jobs.jsonl` | Legacy `fork_sync` jobs emitted from release signals. |
| `flow-events.jsonl` | Generic `FlowEvent` records emitted by flow targets. |
| `flow-dispatches.jsonl` | Dispatch, retry, replay, and failure records. |
Admin endpoints read `flow-events.jsonl` and `flow-dispatches.jsonl`. The feed
poller appends to all relevant files as it accepts new feed entries.

View file

@ -0,0 +1,28 @@
---
title: Packages
description: Workspace packages in the Patch monorepo.
---
# Packages
## `@peezy.tech/patch`
The Bun service in `apps/patch`. It exports no public package API; its runtime
entry point is `src/server.ts`.
Responsibilities:
- Poll configured feeds.
- Normalize entries into `FeedSignal` records.
- Store JSONL state.
- Emit optional Discord notifications.
- Dispatch generic codex-flow events through `@peezy.tech/flow-runtime/client`.
- Serve admin inspection, retry, and replay endpoints.
## `@peezy.tech/patch-docs`
The Tome documentation package in `docs`. Build it with:
```bash
bun run docs:build
```

View file

@ -0,0 +1,44 @@
---
title: Dispatch a Codex release flow
description: Connect the OpenAI Codex release feed to codex-flow automation.
---
# Dispatch a Codex release flow
Patch was built to let upstream release activity trigger generic codex-flow
automation without putting Codex-specific completion logic into Patch itself.
## 1. Use the release source
The bundled `apps/patch/feed-sources.json` includes
`github-openai-codex-releases`. Its target emits `upstream.release` events with
the upstream repository and release tag in the payload.
## 2. Point Patch at a backend
```bash
PATCH_FLOW_DISPATCH_URL=http://127.0.0.1:7345/events \
PATCH_FLOW_DISPATCH_SECRET=dev-secret \
DATA_DIR=./data \
FEED_SOURCES_PATH=./feed-sources.json \
bun run --filter @peezy.tech/patch start
```
`PATCH_FLOW_DISPATCH_URL` can point at the `/events` endpoint or at the backend
base URL. Patch normalizes either form before it creates the shared flow client.
## 3. Inspect the stored event
```bash
curl http://127.0.0.1:3000/flow-events
```
When `PATCH_ADMIN_TOKEN` is set, include either `Authorization: Bearer <token>`
or `X-Patch-Admin-Token: <token>`.
## 4. Keep completion app-owned
Patch dispatches the generic event. The installed Codex release flow owns the
work that happens next: matching `flow.toml`, running steps, checking gates, and
emitting `FLOW_RESULT`. Product-specific completion stays in that flow package
or its backend worker.

View file

@ -0,0 +1,65 @@
---
title: Watch an upstream release
description: Configure a release feed and store the first Patch flow event.
---
# Watch an upstream release
This tutorial creates the smallest useful release watcher: one upstream release
feed that becomes a stored `upstream.release` flow event.
## 1. Add a feed source
Create or edit `apps/patch/feed-sources.json`:
```json
{
"sources": [
{
"id": "github-openai-codex-releases",
"provider": "github",
"url": "https://github.com/openai/codex/releases.atom",
"event": "release",
"repo": {
"owner": "openai",
"name": "codex",
"fullName": "openai/codex",
"webUrl": "https://github.com/openai/codex",
"defaultBranch": "main"
},
"target": {
"mode": "flow_dispatch",
"eventType": "upstream.release",
"dispatchUrlEnv": "PATCH_FLOW_DISPATCH_URL",
"dispatchSecretEnv": "PATCH_FLOW_DISPATCH_SECRET",
"payload": {
"provider": "github",
"repo": "openai/codex"
}
}
}
]
}
```
## 2. Start Patch
```bash
DATA_DIR=./data \
FEED_SOURCES_PATH=./feed-sources.json \
bun run --filter @peezy.tech/patch dev
```
The first poll primes `data/feed-state.json`. By default, old feed entries are
not emitted on that first pass.
## 3. Dispatch new releases
When the feed later contains an unseen release entry, Patch appends:
- `data/feed-events.jsonl` for the normalized signal.
- `data/flow-events.jsonl` for the generic flow event.
- `data/flow-dispatches.jsonl` for the dispatch outcome.
If `PATCH_FLOW_DISPATCH_URL` is not set, Patch uses local flow execution from
the working directory. If it is set, Patch sends the event to the HTTP backend.