- Go 81.1%
- go-html-template 10.7%
- JavaScript 2.7%
- CSS 2%
- TypeScript 1.9%
- Other 1.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .devcontainer | ||
| .forgejo | ||
| .github/workflows | ||
| .semgrep | ||
| .w3ds | ||
| assets | ||
| branding | ||
| build | ||
| cmd | ||
| contrib | ||
| custom/conf | ||
| docker | ||
| docs/gitw3 | ||
| models | ||
| modules | ||
| options | ||
| public | ||
| release-notes | ||
| release-notes-published | ||
| releases/images | ||
| routers | ||
| services | ||
| templates | ||
| tests | ||
| tools | ||
| web_src | ||
| .air.toml | ||
| .deadcode-out | ||
| .dockerignore | ||
| .editorconfig | ||
| .envrc.example | ||
| .gitattributes | ||
| .gitignore | ||
| .gitmodules | ||
| .gitpod.yml | ||
| .golangci.yml | ||
| .ignore | ||
| .mailmap | ||
| .markdownlint.yaml | ||
| .mockery.yml | ||
| .node-version | ||
| .npmrc | ||
| .release-notes-assistant.yaml | ||
| .spectral.yaml | ||
| .yamllint.yaml | ||
| BSDmakefile | ||
| CODEOWNERS | ||
| CONTRIBUTING.md | ||
| DCO | ||
| Dockerfile | ||
| Dockerfile.rootless | ||
| eslint.config.mjs | ||
| flake.lock | ||
| flake.nix | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| Makefile | ||
| manifest.scm | ||
| package-lock.json | ||
| package.json | ||
| playwright.config.ts | ||
| README.md | ||
| release-notes-assistant.sh | ||
| RELEASE-NOTES.md | ||
| shell.nix | ||
| stylelint.config.js | ||
| tailwind.config.js | ||
| tsconfig.json | ||
| vitest.config.ts | ||
| webpack.config.js | ||
GitW3
GitW3 is MetaState's self-hosted Git forge. It is a fork of Forgejo, rebranded for the MetaState / W3DS ecosystem.
Relationship to Forgejo
GitW3 tracks Forgejo releases. Everything that makes GitW3 GitW3 is deliberately confined to a thin layer on top of upstream:
- Branding — application name, logo, colour scheme, templates and locale strings. Applied by
branding/apply.sh, which rewrites upstream files in the working tree immediately beforemake build. Those rewrites are never committed, so the tracked diff against Forgejo stays empty. Forgejo's owncustom/mechanism cannot do this job: the Docker image pointsGITEA_CUSTOMat a volume, so the repository'scustom/directory is never read in a container, and the assets that need overriding are embedded into the binary at build time by thebindatatag anyway. - Nothing else, for now. In particular, W3DS login is not implemented in this fork. It is provided by a separate OIDC bridge service and wired up through Forgejo's built-in OAuth2 authentication sources, so it needs no change to this codebase.
Keeping the patch surface near zero is the whole strategy. Every line we diverge from upstream is a line that can conflict when the next Forgejo security release has to be merged.
Note that the Go module path is forgejo.org and the built binary is named gitea upstream. We
override the binary name at build time (EXECUTABLE=gitw3) and leave both alone in the source —
renaming them for real would mean touching thousands of lines for no user-visible gain.
Branches
| Branch | Owner | Purpose |
|---|---|---|
main |
us | GitW3. Branched from upstream v16.0.2; carries our commits. |
forgejo |
upstream | Forgejo's development branch, mirrored verbatim. Do not commit here. |
v*/forgejo |
upstream | Forgejo release branches, mirrored verbatim. Do not commit here. |
v* (tags) |
upstream | Forgejo release tags, mirrored verbatim. |
gitw3-v* (tags) |
us | GitW3 releases. |
Upstream refs are refreshed daily by .github/workflows/upstream-sync.yml,
which only ever writes to upstream-owned refs and never to main.
Keeping up with upstream
See docs/gitw3/upstream-sync.md for the merge procedure.
This is not optional maintenance. Forgejo ships security releases regularly — v16.0.2 and v15.0.6 both landed on 2026-07-30 — and an un-upgraded forge exposed to the internet is a liability.
Building
Requires Go (version pinned in go.mod) and Node (pinned in
.node-version).
make deps-frontend
./branding/apply.sh
EXECUTABLE=gitw3 TAGS="bindata sqlite sqlite_unlock_notify" make build
./gitw3 --version
git checkout -- cmd/ docker/ modules/ options/ public/ routers/ services/ templates/ web_src/
The branding step is not optional: without it make build produces a binary that still calls
itself Forgejo. The last line puts the working tree back — see
branding/README.md.
Container images are published to ghcr.io/ensombl/gitw3.
Local development
For iterating on code, use the live-reload dev server instead of the full build above:
make deps-frontend
TAGS="sqlite sqlite_unlock_notify" GITEA_RUN_MODE=dev make watch
make watch runs the frontend (webpack --watch) and backend (air, which rebuilds and restarts
on .go/.tmpl changes) together. TAGS must be set explicitly here and include sqlite: unlike
the release build above, make watch's backend target does not set it, so without it the install
wizard won't offer SQLite3 as a database option.
First run serves the install wizard at http://localhost:3000. If that port is taken, set
[server] HTTP_PORT in custom/conf/app.ini — environment variable overrides
(GITEA__server__HTTP_PORT) are not read this early, only app.ini is. SQLite needs no other setup.
Testing W3DS login locally
GitW3 owns an always-visible Continue with W3DS button and the stable /user/login/w3ds entry
point. The cryptographic wallet flow remains in the separate w3ds-oidc-bridge implemented by the
MetaState team in
MetaState-Prototype-Project/prototype#1102.
The bridge is wired into Forgejo as an OAuth2 authentication source named exactly W3DS; GitW3
keeps the native button visible and reports a clear configuration error when that source is absent.
To exercise the complete flow locally:
- Run the OIDC bridge service (out of scope for this repo) and note its client ID, client secret,
and
.well-known/openid-configurationdiscovery URL. - Register it as an authentication source. The name must be exactly
W3DS— it's baked into the bridge's redirect URI as<ROOT_URL>/user/oauth2/W3DS/callback:
The./gitea admin auth add-oauth \ --name "W3DS" \ --provider "openidConnect" \ --key "<client id>" \ --secret "<client secret>" \ --auto-discover-url "http://<bridge-host>/.well-known/openid-configuration" \ --scopes "openid" --scopes "profile" --scopes "email"profile/emailscopes are needed so Forgejo gets a real username/email back instead of falling back to the OIDCsubclaim and a synthetic@w3ds.invalidaddress. - Add the following to
custom/conf/app.iniand restart (app.iniis only read at process startup, so this needs a restart ofmake watch, not just a hot-reload):
Without[oauth2_client] ENABLE_AUTO_REGISTRATION = true ACCOUNT_LINKING = login USERNAME = nickname REGISTER_EMAIL_CONFIRM = falseENABLE_AUTO_REGISTRATION, Forgejo shows a manual "Complete new account" step on every first-time OAuth2 login instead of silently provisioning the account, which is the intended production behaviour.
Testing code sync locally
Code sync isn't part of this codebase either — a separate forgejo-code-sync service resolves each push's
author via the W3DS link above and writes their commits into their eVault, using a Forgejo system webhook and
the admin Users API. To exercise it locally:
- Create a dedicated site-admin service account for the sync service — not a shared human admin's login, since
its token can read every account's
login_nameand every private repo's content. - Generate two PATs on that account:
read:user,read:repositoryscopes, for the service's continuous use (FORGEJO_ADMIN_TOKEN).write:adminscope, for the one-time webhook registration below (FORGEJO_PROVISIONING_TOKEN) — optional, falls back to the token above, but keeps the always-running token's blast radius smaller.
- If the sync service's webhook URL resolves to loopback relative to this instance (true for local dev, not
for a real deployment), add the following to
custom/conf/app.iniand restart:[webhook] ALLOWED_HOST_LIST = loopback - Run the sync service's registration script once (idempotent, safe to re-run on redeploy) to register the
system webhook via
POST /api/v1/admin/hooks. - Two things about that endpoint worth knowing, even though the script already handles both:
activemust be sent astrueexplicitly — it defaults tofalse, and a hook created without it looks completely normal (201, listed in Site Administration) but never delivers anything.config.is_system_webhookmust be the literal string"true"— omit it and GitW3 silently creates a "default" webhook instead: invisible toGET /admin/hooks, and it only applies to repos created after it's added, never retroactively to existing ones.- Rotating the webhook secret needs the hook deleted and recreated, not
PATCHed —PATCH /admin/hooks/{id}silently ignores a changedconfig.secret.
- Verify after registering: Site Administration → Webhooks shows the hook with Active on, not just present, and a "Test Delivery" (or a real push) actually reaches the service.
- Don't register the webhook twice — two system webhooks pointed at the same URL produce two envelopes per push; the sync service has no deduplication for that case by design.
Platform onboarding and Marketplace publication
The New action now offers guided W3DS platform creation. New-platform repositories receive a
versioned .w3ds/platform.json, optional browser-generated identity keys, and the documented W3DS AI
skill quick install. A companion process provisions the platform eName and keeps its Marketplace
PlatformProfile synchronized from the default branch.
See docs/gitw3/platform-onboarding.md for the manifest contract, service deployment, system webhook, credentials, status integration, and failure behavior.
Licence
Forgejo is GPL-3.0-or-later, and so is GitW3. See LICENSE.
The Forgejo branding is not covered by that licence and is not ours to reuse: the logo is CC BY-SA 4.0 by Caesar Schinas, and the Jo mascot is CC BY 4.0 by David Revoy. The attribution exemption on the logo is granted to the Forgejo project only. GitW3 branding assets must therefore be original work, not derivatives of Forgejo's.
Upstream
- Source: https://codeberg.org/forgejo/forgejo
- Documentation: https://forgejo.org/docs/latest/