AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
release-log lässt deinen MCP-Client die Commits in Release Notes übersetzen. Du liest gegen und veröffentlichst, ohne selbst eine Datei im Repo anzufassen.
Translation pending
Developer toolsTypeScript · VueGitHub @czepterOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Release logs that live in your GitHub repo. Agents write them over MCP, humans edit them in the web UI, and every log is served as public JSON — versions, releases and media — backed by a GitHub App that keeps the index in sync.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
release-log turns a GitHub repository into a release log. The releases are
plain JSON files in the repo — release-log.json, releases/*.json and a
media/ folder — so the source of truth is version-controlled, reviewable and
yours. The service indexes those files and serves them as a public JSON API,
plus a web page for readers.Read the full README ↗release-log turns a GitHub repository into a release log. The releases are
plain JSON files in the repo — release-log.json, releases/*.json and a
media/ folder — so the source of truth is version-controlled, reviewable and
yours. The service indexes those files and serves them as a public JSON API,
plus a web page for readers.
Three ways to write a release:
list_logs, get_log,
get_release, create_log, write_release, publish_release,
unpublish_release and add_media. Point Claude or any MCP client at
/mcp, let it draft the release from the commits, and it commits to the repo
the same way a human would.npm run dev # Nuxt with a fake GitHub from logs/demo, http://localhost:3000/api/dev/login
npm test # node --test, no arguments, from the repository root
npm run typecheck
npm run build # Nuxt build into web/.output
npm start # the built service: Nuxt with the core embedded (PORT, default 3000)
npm run start:core # the core alone, without the interface, for tests and machine clients
SELFHOSTING.md covers it end to end: the GitHub App and its permissions, the secrets, and one section per deployment method — Docker Compose, Coolify, Railpack, Dokku and CapRover, plain Node under systemd — plus the reverse proxy, backups and what each startup error means.
cp .env.example .env # fill it in
docker compose up -d --build
Nuxt (web/) is the only interface and embeds the core (server.ts, lib/)
in the same process. What humans see is rendered by Nuxt; what machines speak
is answered by the core, unchanged (web/server/utils/corePaths.ts):
| Core | Nuxt |
|---|---|
/health, /webhook, /mcp, /me, /auth/*, /.well-known/*, /upload/* | /dashboard, /dashboard/logs/<id>, release editor, /konto |
/oauth/register, /oauth/token, POST /oauth/authorize | GET /oauth/authorize (consent), /anmeldung |
/l/<id>/versions, /l/<id>/releases…, /l/<id>/media/… | /l/<id>, /l/<id>/r/<version> |
/api/* — the page API from lib/api/ | |
/ — marketing page; with a session, its buttons lead to the dashboard |
OPEN_SIGNUP=1 (optional) opens signup: every GitHub account may sign in and
the allowlist is no longer consulted. Without the variable the allowlist
applies as before, and without an entry nobody gets in except the accounts in
ADMIN_LOGINS.
The interface speaks German and English. The language lives in the rl_lang
cookie; without a cookie Accept-Language decides, and without a matching
language it is German. The strings live in web/app/i18n/de.ts and en.ts --
de.ts is the source of truth, en.ts is type-checked against it, and
messages.test.ts keeps both on the same set of keys. The machine-facing
surface is untouched: the section labels in the JSON (lib/sections.ts) stay
German, and the interface translates them through the stable key.
MAX_LOGS_PER_OWNER (optional, default 10) limits how many logs an account may
keep. The limit counts per repository owner and applies to newly created as
well as adopted repositories. 0 lifts it.
RL_SHOWCASE_URL (optional) embeds one log's changelog into the landing page:
the URL of its public page, https://<host>/l/<id>, from another instance too.
If it is missing or the source does not answer, the landing page appears
without that section. The result is cached for five minutes.
Nuxt reads the migrations from MIGRATIONS_DIR (default ./drizzle, relative
to the working directory): in the bundled server, import.meta.url no longer
points next to drizzle/.
npm run dev sets RL_DEV_FAKE=1: the core runs against a fake GitHub from
logs/demo, and /api/dev/login?as=admin signs in without GitHub. Neither
exists in the production build (import.meta.dev).
| Route | Response |
|---|---|
/health | {"ok":true} |
/l/<id>/versions | Every version with date, title and link |
/l/<id>/releases?page=&per_page= | Feed with per-section counts, 100 per page at most |
/l/<id>/releases/<version> | One version with all entries |
/l/<id>/media/<path> | Images a version refers to |
A log is a directory under LOGS_ROOT holding release-log.json,
releases/*.json and media/. The public identifier is the id in
release-log.json, not the directory name.
covered appears in no response: it is bookkeeping, not content.
npm run reindex -- <owner>/<repo> [...]
Reads each named repository through the installed GitHub App and writes its log
index into the SQLite database (DB_PATH, default
./data/release-log.sqlite). The values come from .env — the script reads
the file itself when it finds one. readConfig requires four environment
variables:
| Variable | Meaning |
|---|---|
GITHUB_APP_ID | The app ID of the GitHub App |
GITHUB_APP_PRIVATE_KEY | Its private key, PEM, base64-encoded |
GITHUB_WEBHOOK_SECRET | The webhook secret of the app |
BASE_URL | The public base URL of the service |
TOKEN_ENCRYPTION_KEY | 32 bytes, hex- or base64-encoded: encrypts the GitHub user tokens (openssl rand -hex 32) |
The service reconciles itself. Two triggers start the same function:
POST /webhook, signed with GITHUB_WEBHOOK_SECRET. A delivery
without a valid signature is dropped before its body is read. The service
answers 202 immediately; the reconcile runs afterwards.indexed_at, plus
every log that has not been indexed for more than an hour. Frozen logs are
included — that is the only way a log thaws once its repository comes back.A lost webhook therefore costs one reconcile round at most. The reconcile compares git blob SHAs, not an event log: "the webhook never arrived" and "rebuild the index from zero" are the same case.
| Variable | Meaning |
|---|---|
DB_PATH | Path of the SQLite file, default ./data/release-log.sqlite |
PORT | Port of the service, default 8787 |
GITHUB_CLIENT_ID | Client ID of the GitHub App, for signing in |
GITHUB_CLIENT_SECRET | Client secret of the GitHub App, for signing in |
SIGNING_KEY | Signs the session cookie |
ADMIN_LOGINS | GitHub logins with admin rights, comma-separated — at least one is required |
GET /health answers 200 without touching the index.
GET /auth/github/login redirects to GitHub.GET /auth/github/callback (registered with GitHub as the callback URL)
receives the answer, checks the allowlist, sets a session cookie on success
(30 days) and redirects to the dashboard. If signing in fails, it goes to
/anmeldung?fehler=state|github|denied.POST /auth/logout deletes the cookie.GET /me answers {login, isAdmin} for a valid session, otherwise 401. The
account page for humans is /konto (Nuxt).GET / redirects to the dashboard.Admin rights come from ADMIN_LOGINS and nowhere else — there is no database
column for them. Everyone else who is allowed in sits in the allowlist table;
admins manage it under /konto.
Whether an account has write access to the repository behind a log is checked
against GitHub and cached for five minutes per (account, log). The cache is
invalidated earlier when member, installation_repositories or
installation arrives — a membership or installation change can affect exactly
that right. A transfer or deletion of the repository (through the repository
event, which only triggers the content reconcile) does not: a cached "yes" can
survive such an event by up to five minutes, the same five-minute ceiling as
everywhere else.
Nuxt pages over the page API (/api/*, session cookie, JSON). The rules live
in lib/api/:
/dashboard — the logs the signed-in account may write to (frozen ones
additionally for admins). "New log" creates a repository through the same
path as create_log./dashboard/logs/<id> — status, reconcile errors, releases, settings
(view, visibility, curation_notes as a commit on release-log.json,
never as a database write), media upload (.png, .jpg, .webp, 10 MB at
most, creates, never replaces) and permanent deletion by typing the product
name./dashboard/logs/<id>/releases/new, …/releases/<version> — the release
editor (Editor.js): headline, paragraphs, one image and the changes as
blocks. Saving commits through the same path as write_release, against the
blob SHA that was last read; if the release has changed since, nothing is
overwritten. Publish and unpublish sit next to it. The editor never touches
covered and commits./konto — profile, disconnecting connected MCP clients, the allowlist
(admins).Mutating /api calls require content-type: application/json (upload:
x-filename); with SameSite=Lax no foreign page can trigger them.
A dedicated OAuth 2.0 authorization server (spec §5, "role 2") protects /mcp:
POST /oauth/register — Dynamic Client Registration (RFC 7591), public
clients only (no secret, PKCE S256 is mandatory).GET /oauth/authorize — consent screen (Nuxt), POST redeems the decision
in the core. Both check through lib/oauthRequest.ts.POST /oauth/token — authorization_code and refresh_token grant.GET /.well-known/oauth-protected-resource/mcp,
GET /.well-known/oauth-authorization-server — metadata (RFC 9728/8414)./konto.POST /mcp — the MCP surface itself, Streamable HTTP, bearer token required:
list_logs, get_log, get_release (scope logs:read), create_log,
write_release, publish_release, unpublish_release, add_media (scope
logs:write).PUT /upload/<token> — the image path of add_media. The token in the URL
is the entire credential: single-use, valid for ten minutes, bound to log,
target path and account. On upload the route checks again whether that
account may still write, limits to 10 MB and creates the file instead of
replacing an existing one (path_exists, since otherwise a new image would
silently change every published release pointing at it).This way image data never touches the agent's context — 5 MB would come to roughly 6.7 MB of text as base64.
create_log is the one path in this service that does not run on the
installation token: POST /user/repos exists only for user tokens. The service
therefore keeps the user token from signing in, per account — encrypted
with TOKEN_ENCRYPTION_KEY, not hashed, because it is used and not merely
checked. That makes it the only secret in the system a database theft captures
in usable form.
create_log answers reauth_required and names the
sign-in URL. The human signs in once more.Creating runs on the user token, writing on the installation token: first the
repository, then the reachability check (repo_not_installed if the app cannot
see the fresh repository), then release-log.json and README.md, then the
reconcile. The answer comes only once the log is in the index.
/l/<id> — Nuxt, server-rendered as a timeline: version and date on the
left, sections (Wichtig, Neu, Änderungen, Behoben) to expand. timeline
opens the first section per release, full opens all of them. Drafts only
for signed-in accounts with write access; private logs carry noindex and
answer everyone else with a 404, like a missing log./l/<id>/r/<version> — permalink to a single version.The public JSON feed (/l/<id>/versions, /l/<id>/releases, …) and the media
download stay in the core.
GNU AGPL 3.0. You may run, change and share release-log-hub, also for money. If you run a changed version for others, offer them its source code under the same license. For terms without that duty, ask the author for a commercial license.