entries.ts503 lines · main
1export interface ChangelogEntry {
2 date: string; // ISO yyyy-mm-dd
3 tags: readonly ChangelogTag[];
4 title: string;
5 body: string;
6}
7
8export type ChangelogTag = 'feat' | 'fix' | 'security' | 'docs' | 'infra' | 'chore';
9
10export const CHANGELOG_ENTRIES: readonly ChangelogEntry[] = [
11 {
12 date: '2026-07-21',
13 tags: ['docs', 'infra'],
14 title: 'Doltgres-first docs + setup/connect split + Auth/S3 handoff accuracy',
15 body: 'Public docs, MCP briven_ask guides, handoff, dashboard connect copy, and CLI help now match production: control + project SQL on Doltgres (not dual-engine forever); files on MinIO S3 (s3.briven.tech); `briven setup` = new project only, `briven connect` = existing (no setup --project); three key families (pk_briven_auth_, brk_, storage brvn, MCP pk_briven_mcp_). Handoff gate remains open for Auth + project S3.',
16 },
17 {
18 date: '2026-07-18',
19 tags: ['feat', 'security', 'docs'],
20 title: 'S6 auth reliability bar — metrics, tests, admin snapshot',
21 body: 'Closes the code/ops half of the Clerk-gap reliability sprint: process counters + Prometheus briven_auth_* series (rate-limit denials, memory fallback, mailer failures, auth 5xx); GET /v1/admin/auth-reliability; admin Health panel section; unit tests for memory rate limits and per-project isolation; scripts/s6-auth-verify.sh for read-only platform probes. Human browser AUTH-GO-LIVE + second-project check still required before “friends can rely on auth.” Auto-deploy remains OFF.',
22 },
23 {
24 date: '2026-07-18',
25 tags: ['docs', 'security'],
26 title: 'beta next steps: auth checklist track + S6 reliability notes',
27 body: 'BETA-V1-NEXT-STEPS.md tracks auth go-live, second-project isolation, and dogfood. docs/S6-RELIABILITY.md documents Redis rate-limit fail-open behavior and customer status blurbs. Status pages explain “if sign-in feels broken.” Platform probe: production /ready redis ok. Human browser checklist rows 1–4+7 still required before “friends can rely on auth.”',
28 },
29 {
30 date: '2026-07-18',
31 tags: ['feat', 'docs'],
32 title: 'briven setup — Convex-style one-command connect + project wiring',
33 body: 'New CLI command `briven setup` signs in via browser OAuth, creates a new cloud project or attaches an existing one (`--name` / `--project`), scaffolds the folder, mints a CLI key, and writes briven.json. Bare `briven` with no linked project starts setup. Docs (connect, quickstart, cli, templates), dashboard empty-state, and project connect panel all promote this path. Templates remain optional starters only — not the product model. Manual `login` + `link` still works for CI/key paste.',
34 },
35 {
36 date: '2026-05-12',
37 tags: ['feat', 'docs'],
38 title: 'AI.md operator setup guide + per-feature model overrides',
39 body: 'docs/AI.md (tracked in repo alongside docs/MIGRATION.md) documents which ollama model to run, why qwen2.5-coder:32b is the default, how to differentiate models per feature, hardware sizing reference, what NOT to pick. four new env vars — BRIVEN_OLLAMA_MODEL_SCHEMA / _FUNCTION / _EXPLAIN / _DOCS — each falls back to BRIVEN_OLLAMA_MODEL when unset so single-model operators don\'t need to set them. logs now record the actual model used per request (not the global default).',
40 },
41 {
42 date: '2026-05-12',
43 tags: ['fix'],
44 title: 'mobile responsive pass — dashboard nav, project tabs, marketing footer',
45 body: 'project tabs row (14 secondary tabs inside a project — overview / functions / logs / etc.) now horizontally scrolls on mobile instead of overflowing; sidebar hides below md and a horizontal nav strip appears at the top of the dashboard so projects / teams / billing / settings stay one tap away on phones. admin stats grid + project overview cards + invoke-panel grid all gain responsive col counts. subprocessors table wraps in overflow-x. marketing footer rewritten as a proper five-column nav (brand + product + docs + legal) on top of a thin bottom strip with copyright + flndrn limited + heart-in-flanders. fixed dashboard body padding so phones get the full viewport width.',
46 },
47 {
48 date: '2026-05-12',
49 tags: ['feat'],
50 title: 'briven ai cli — schema / function / explain from the terminal',
51 body: '`briven ai schema "..."`, `briven ai function "..."`, `briven ai explain --file <path>` proxy to the same three api endpoints the dashboard hits. takes a prompt as a positional arg, writes to stdout (or `--out path`). `briven ai function --with-schema` pulls the project\'s current schema snapshot in as model context. exit code 2 means the api host hasn\'t wired BRIVEN_OLLAMA_URL — a clear "AI assistant offline" message instead of an opaque http error. completes the AI surface: api + dashboard + cli all wire to the same self-hosted Qwen 2.5-coder backend.',
52 },
53 {
54 date: '2026-05-12',
55 tags: ['feat', 'docs'],
56 title: 'docs search + corpus index (AI assistant pre-staged)',
57 body: 'new endpoint at /api/search?q=… returns the top N docs pages by word-overlap against a hand-curated corpus (27 entries covering every published page). /search renders the same results as a browseable page with a search box. when the ollama backend lands the AI docs assistant wraps this same ranking — the search picks the top 3 pages, the assistant forwards them to the model as system-prompt context.',
58 },
59 {
60 date: '2026-05-12',
61 tags: ['docs'],
62 title: 'migration · mongodb → briven',
63 body: 'new page at /migration/mongodb. covers the decision matrix for keeping embedded docs as jsonb vs flattening into separate tables, ObjectId → text + ulid for new ids, the mongoexport → transform-script → COPY data move, find/aggregate → ctx.db chain rewrites, and the 2+ week parallel-run window mongo → relational migrations need to catch shape mismatches.',
64 },
65 {
66 date: '2026-05-12',
67 tags: ['feat'],
68 title: 'ai explain code — third feature in the AI trifecta',
69 body: 'paste any briven schema or function snippet, get a plain-english walkthrough in briven idioms (what the wrapper means, which db calls happen, where reactivity hooks in, sharp edges). same self-hosted Qwen 2.5-coder backend + same not-logged privacy posture as schema/function gen. dashboard surface at /dashboard/projects/:id/ai-explain. third tab in the AI section.',
70 },
71 {
72 date: '2026-05-12',
73 tags: ['docs'],
74 title: 'per-source migration sub-pages — drizzle + prisma',
75 body: 'two new pages on docs.briven.tech: /migration/drizzle and /migration/prisma. each covers schema-column mapping, indexes, data export (pg_dump + pg_restore), the functions-port rewrite (drizzle\'s db.select chains and prisma\'s findMany calls both become ctx.db chains), auth hand-off to the nextauth guide, and reactivity as a new capability. the /migration overview separates them out of the lumped "raw postgres / drizzle / prisma" entry now that each has its own page.',
76 },
77 {
78 date: '2026-05-12',
79 tags: ['feat'],
80 title: 'ai function generator — companion to ai schema',
81 body: 'natural-language → draft briven/functions/<name>.ts. /v1/projects/:id/ai/generate-function takes { prompt, schemaContext? } and forwards to ollama with a system prompt baking in the @briven/cli/server DSL (query / mutation / action wrappers, ctx.db chains, brivenError shape, ulid prefixing). dashboard surface at /dashboard/projects/:id/ai-function ssrs the current schema snapshot into a compact "table { col: type PK NOT NULL -> fk }" summary the model reads as context. the same not_configured 503 + privacy stance as the schema generator: prompts and responses are not logged.',
82 },
83 {
84 date: '2026-05-12',
85 tags: ['feat'],
86 title: 'tier-aware realtime subscription cap + project cap-warning banner',
87 body: 'TIERS.concurrentSubscriptions (free=100 / pro=1000 / team=10000) is now enforced by the realtime service, replacing the platform-wide env ceiling. realtime resolves the cap from a new internal endpoint /v1/internal/projects/:id/limits on first subscribe per project (5-min cache, shared-secret gated). project overview page grows a yellow/red banner at 75%/90% of the cap so the user can upgrade before subscribes start getting rejected; banner sources from a new /v1/projects/:id/realtime-stats endpoint scoped to the caller\'s own project. admin · realtime page auto-refreshes every 10s via meta refresh so an operator can keep it open while watching a noisy project.',
88 },
89 {
90 date: '2026-05-12',
91 tags: ['feat'],
92 title: 'admin · usage — retry skipped polar pushes',
93 body: 'when polar push rows land in `skipped` (missing meter id, bad customer mapping, durable 4xx), the operator had to UPDATE usage_events directly in psql to re-enable them. now /dashboard/admin/usage grows a "retry → pending" button (window: 1/7/30/90 days) that flips skipped rows back to pending in one click. window is bounded server-side so a runaway click can\'t re-push years of stale data. action is audit-logged with the row count.',
94 },
95 {
96 date: '2026-05-12',
97 tags: ['feat'],
98 title: 'admin · realtime — live subscription snapshot',
99 body: 'new admin page at /dashboard/admin/realtime shows per-project subscription counts + per-channel refcounts + the current cap values, with severity coloring at 60% and 90% of the per-project cap so an operator can spot a noisy project before it gets clamped. backed by a shared-secret-gated /v1/realtime/stats on the realtime service (no PII leaked — only project ids and channel names). 503 with a clear "not configured" state when the realtime URL or shared secret is unset.',
100 },
101 {
102 date: '2026-05-12',
103 tags: ['security'],
104 title: 'subscription caps on realtime — per-ws + per-project',
105 body: 'realtime now refuses subscribe frames past BRIVEN_REALTIME_MAX_SUBS_PER_WS (default 200) and BRIVEN_REALTIME_MAX_SUBS_PER_PROJECT (default 10,000 — the year-one platform ceiling). counter briven_realtime_subscribe_rejected_total{reason="ws_limit"|"project_limit"} lights up on /metrics so an operator can spot a runaway client. error frames carry the offending subscriptionId so the client can correlate. closes the "single bad ws can exhaust 10k concurrent subs" surface noted in BUILD_PLAN §7.4.',
106 },
107 {
108 date: '2026-05-12',
109 tags: ['feat'],
110 title: 'connection-seconds in usage + tier caps',
111 body: 'TIERS now ships connectionSecondsPerMonth (1M / 10M / 100M for free / pro / team) and concurrentSubscriptions (100 / 1000 / 10000). hourly aggregator scrapes briven_realtime_connection_seconds_total from the realtime /metrics endpoint, diffs per project, writes connection_seconds rows to usage_events (the polar push worker already drains them). dashboard usage card now shows realtime usage alongside invocations + storage + compute, formatted as s/m/h/d so a Team plan reads as "115d / 1158d" instead of an unreadable 100 million seconds.',
112 },
113 {
114 date: '2026-05-12',
115 tags: ['feat'],
116 title: 'polar meter push: real fetch + org→customer resolution',
117 body: 'the metering worker no longer logs intent — it actually posts to polar. customer ids resolve through project → org → subscriptions.polar_customer_id (5-min in-process cache, invalidated on the polar webhook so a fresh checkout takes effect on the next tick). 5xx + network errors leave the row pending for the next minute; 4xx marks the row skipped so it doesn\'t loop forever. invocations + storage_bytes flow today; connection_seconds queued for the realtime /metrics scraper.',
118 },
119 {
120 date: '2026-05-12',
121 tags: ['feat'],
122 title: 'storage caps on every tier, surfaced through usage',
123 body: 'TIERS now ships storageBytes alongside projectsPerOrg / functionsPerProject / invokesPerMonth: 1 GiB on free, 10 GiB on pro, 100 GiB on team. dashboard usage card shows "used / cap" — pg_total_relation_size sampled live against the project schema, excluding the platform\'s _briven_* bookkeeping.',
124 },
125 {
126 date: '2026-05-12',
127 tags: ['security'],
128 title: 'project-suspension gate mounted globally',
129 body: 'blockIfProjectSuspended is now mounted once at /v1/projects/:id/* instead of sprinkled per-router. read methods (GET/HEAD/OPTIONS) skip the check so dashboards stay readable while a project is being investigated; every mutation route (env writes, member moves, studio writes, db shell, deploys, invokes) inherits the gate without per-route wiring. closes the drift risk where a new route lands without picking up the suspension check.',
130 },
131 {
132 date: '2026-05-12',
133 tags: ['feat', 'security'],
134 title: 'account deletion (gdpr article 17) — 30-day soft-delete cascade',
135 body: "settings · danger zone now ships a real account-deletion flow. typed-email confirmation gates the click; the api revokes every session, every api key on sole-ownership projects, and every pending invitation the user sent. sole-owner orgs (personal + any team where the user is the only owner) and the projects under them soft-delete. multi-owner team orgs survive — the user is just removed from membership. PII clears in the same transaction (legal name, address, VAT, company, display name, image); id + email + createdAt stay so audit-log FKs survive. confirmation email lands before the cascade runs. a daily 03:30 UTC worker hard-deletes rows past the 30-day grace window; FK CASCADE handles the rest. polar subscriptions are NOT auto-cancelled — manage via the polar portal during the grace window.",
136 },
137 {
138 date: '2026-05-12',
139 tags: ['infra'],
140 title: 'alertmanager + discord bridges in the observability stack',
141 body: 'prometheus alerts now route through alertmanager → benjojo/alertmanager-discord bridges → two discord channels. severity=critical|warning lands in #briven-alerts (page-worthy); info lands in #briven-deploys. operator pastes DISCORD_WEBHOOK_ALERTS + DISCORD_WEBHOOK_DEPLOYS into the dokploy env; the bridges hold them inside the docker network so urls never leave the host. group_wait 30s, group_interval 5m, repeat_interval 4h, plus an inhibit rule so a critical for (service, alertname) suppresses redundant warnings.',
142 },
143 {
144 date: '2026-05-12',
145 tags: ['docs'],
146 title: 'http api reference at /docs/api',
147 body: 'every public endpoint grouped by area (invoke, realtime, projects, deployments, studio, logs + stats, usage, api keys, project members, orgs, billing) with method, path, and a one-line summary. linked from the docs sidebar.',
148 },
149 {
150 date: '2026-05-12',
151 tags: ['feat'],
152 title: 'studio polish: ALTER COLUMN, TRUNCATE, schema overview, project move, invocation sparkline',
153 body: 'ALTER COLUMN endpoint toggles nullability and changes/drops default expressions with a 2-mode PATCH (rename OR alter). TRUNCATE TABLE button (RESTART IDENTITY, opt-in CASCADE) for wiping rows without dropping the schema. New /studio/schema page renders every table in one canvas with FK arrows + inbound references. Project settings grows "move to another team" — re-parents a project across orgs after verifying target-org membership. Project overview gains a 24-hour invocation sparkline (server-rendered SVG, no client JS) with red overlay for errors.',
154 },
155 {
156 date: '2026-05-12',
157 tags: ['feat'],
158 title: 'function logs + per-function stats in the dashboard',
159 body: "new /logs tab on every project surfaces function_logs (already populated by the runtime log-fanout worker) with chip filters per function and ok/err status, cursor-based pagination, and a JSON export that walks the cursor for up to 1000 rows honoring the active filter. Per-function stats badge on the functions tab shows last-24h count + error rate + p50/p99 duration via a single percentile_cont query. Project overview surfaces last 5 errors in a red-tinted card with a deep-link to the filtered logs view.",
160 },
161 {
162 date: '2026-05-12',
163 tags: ['feat'],
164 title: 'dashboard-managed schemas — convex-style manual database creation',
165 body: "studio gains the full DDL surface: + new table with typed columns (text/integer/bigint/boolean/timestamptz/jsonb/uuid/numeric), primary keys, foreign keys with ON DELETE behavior, defaults; add/drop column on a live table; create/drop indexes (multi-column + unique). Schema panel renders relationships as “→ users.id”. FK values in the data view become “↗” links to the referenced row. Copy-as-schema.ts generates the equivalent briven/schema.ts so anything you click can graduate to git. SQL editor at /studio/sql runs arbitrary statements scoped via SET LOCAL ROLE to the project owner role, 5s statement_timeout, every query audit-logged. New connect tab shows project endpoints + a button to issue a 15-minute shell DSN for psql / pgcli / external tools. CLI path unchanged; both write to the same schema.",
166 },
167 {
168 date: '2026-05-12',
169 tags: ['feat'],
170 title: 'team org admin — invitations, members, role change, delete',
171 body: 'team owners can now invite collaborators by email (mirrors project_invitations: 7-day hashed-token email link, accept page at /dashboard/org-invitations/accept), list current members, change roles inline (with last-owner protection), remove non-owners, and soft-delete a team org (refused while live projects still exist). Tier-aware: free tier = 1 personal org, no team creation; pro/team = unlimited. Dashboard surfaces pending team invites in the projects banner alongside project invites.',
172 },
173 {
174 date: '2026-05-11',
175 tags: ['feat'],
176 title: 'AI schema generator — Phase 3 differentiator',
177 body: 'POST /v1/projects/:id/ai/generate-schema accepts a natural-language prompt and returns a draft briven schema.ts via a self-hosted Qwen 2.5-coder 32B running on briven infrastructure (no third-party AI provider). Dashboard surface at /dashboard/projects/:id/ai-schema with a copy button. Gated on BRIVEN_OLLAMA_URL — service shows a friendly "AI assistant offline" message until configured. Prompts and responses are not logged.',
178 },
179 {
180 date: '2026-05-11',
181 tags: ['feat'],
182 title: 'github + google OAuth credentials live on briven.tech',
183 body: 'the OAuth providers (Google + GitHub) now have their credentials configured in the production env. signin page shows both buttons; existing magic-link flow continues to work in parallel.',
184 },
185 {
186 date: '2026-05-11',
187 tags: ['feat'],
188 title: 'polar billing wired end-to-end',
189 body: 'production polar configured: access token, webhook secret, and product IDs for briven Pro ($21.99/mo) and briven Team ($99.99/mo). Existing webhook receiver at /v1/billing/webhook now authoritative for tier sync — subscription.* events flip projects.tier in the same transaction. checkout flow + customer portal both live.',
190 },
191 {
192 date: '2026-05-11',
193 tags: ['docs'],
194 title: 'migration guide: nextauth / auth.js → briven',
195 body: 'sixth per-source migration page. schema maps 1:1 (both target Better Auth\'s shape); the work is the api shape (getServerSession → brivenServer.session(), useSession imports). covers account preservation strategies, provider port, callback hooks, and the cutover checklist.',
196 },
197 {
198 date: '2026-05-11',
199 tags: ['feat'],
200 title: 'usage metering: hourly aggregation cron + usage_events table',
201 body: 'phase 3 GA-blocker progress. startUsageAggregator() runs ~5min after every wall-clock hour and writes one usage_events row per (project, hour, metric) for invocations + storage_bytes. Idempotent via the unique index, so a catch-up re-run after a restart is safe. Polar metering push is a separate worker landing once meter IDs are configured.',
202 },
203 {
204 date: '2026-05-11',
205 tags: ['feat'],
206 title: 'realtime connection-seconds metric per project',
207 body: 'apps/realtime tracks per-project cumulative subscription-seconds (closed subs + live deltas) via briven_realtime_connection_seconds_total{project} on /metrics. Drives the eventual Polar metering push for connection-minutes billing.',
208 },
209 {
210 date: '2026-05-11',
211 tags: ['feat'],
212 title: 'project auto-suspension on abuse-report resolution',
213 body: 'projects.suspended_at column + blockIfProjectSuspended() middleware on invoke + deploy routes (403 project_suspended). The /v1/admin/abuse-reports/:id PATCH endpoint now accepts an optional projectId; resolving with resolution=suspended or banned flips the project in one step. Manual /v1/admin/projects/{suspend,unsuspend} endpoints for the non-abuse paths. Admin triage modal in the dashboard surfaces the new projectId input only when the resolution implies suspension.',
214 },
215 {
216 date: '2026-05-11',
217 tags: ['feat'],
218 title: 'github OAuth alongside google + magic link',
219 body: 'sign-in page now wires three auth methods: magic-link via email, Google OAuth, and GitHub OAuth. Each button is gated by NEXT_PUBLIC_BRIVEN_HAS_*_OAUTH so the UI hides providers whose creds aren\'t configured.',
220 },
221 {
222 date: '2026-05-11',
223 tags: ['fix'],
224 title: '/info reports a real sha on dokploy auto-deploys',
225 body: 'health.ts now resolves the commit sha from .git/HEAD when BRIVEN_BUILD_SHA isn\'t passed at image build time (which is exactly the case for dokploy auto-deploys). loose refs + packed-refs both supported. Paired with treating the literal "dev" env value as "fall back to git" sentinel. Promoted to @briven/shared so apps/realtime + apps/runtime use the same chain.',
226 },
227 {
228 date: '2026-05-11',
229 tags: ['feat'],
230 title: 'deploy_history table + /dashboard/admin/deploys',
231 body: 'every api boot now writes one row into deploy_history (service, buildSha, buildAt, env, bootedAt). new admin page renders a timeline with "live" badge on the most recent row so operators can correlate "the bug appeared at 14:32" with "deploy abc1234 went live at 14:30" without ssh-ing to the box. /v1/admin/deploys?service=api&limit=N exposes the raw stream.',
232 },
233 {
234 date: '2026-05-11',
235 tags: ['docs'],
236 title: 'migration guide: hasura → briven',
237 body: 'fifth per-source migration page. postgres half ports for free via pg_dump; the work is the permissions port — every (role, table, action) triple from hasura metadata becomes a guard in function code. covers actions, event triggers, scheduled triggers, remote schemas, auth (preserve-ids vs preserve-jwts) and subscriptions vs briven\'s reactive useQuery.',
238 },
239 {
240 date: '2026-05-11',
241 tags: ['feat'],
242 title: 'deploy_history table + /dashboard/admin/deploys',
243 body: 'every api boot now writes one row into deploy_history (service, buildSha, buildAt, env, bootedAt). new admin page renders a timeline with "live" badge on the most recent row so operators can correlate "the bug appeared at 14:32" with "deploy abc1234 went live at 14:30" without ssh-ing to the box. /v1/admin/deploys?service=api&limit=N exposes the raw stream.',
244 },
245 {
246 date: '2026-05-11',
247 tags: ['fix'],
248 title: '/info reports a real sha on dokploy auto-deploys',
249 body: 'health.ts now resolves the commit sha from .git/HEAD when BRIVEN_BUILD_SHA isn\'t passed at image build time (which is exactly the case for dokploy auto-deploys). loose refs + packed-refs both supported. paired with treating the literal "dev" string as a "fall back to git" sentinel since that\'s the dockerfile ARG default.',
250 },
251 {
252 date: '2026-05-11',
253 tags: ['infra'],
254 title: 'observability stack live + postgres-exporter sidecar',
255 body: 'grafana / loki / prometheus / promtail running on briven.tech kvm4 as compose project briven-obs; postgres-exporter ships pg_stat_* metrics from briven-postgres. all five prometheus jobs (api / runtime / realtime / postgres / prometheus) report up; four starter dashboards (api requests, runtime invocations, realtime subs, postgres health) populated.',
256 },
257 {
258 date: '2026-05-11',
259 tags: ['feat'],
260 title: 'mittera email suppression layer',
261 body: 'new email_suppressions table + service. mittera webhook handler dispatches permanent bounces / complaints / mittera-suppressed events into the suppression list; outbound send short-circuits on suppressed recipients before posting to mittera. admin ui at /dashboard/admin/email-suppressions with manual add / remove.',
262 },
263 {
264 date: '2026-05-11',
265 tags: ['feat'],
266 title: 'mittera outbound + webhook live on briven.tech',
267 body: 'POST https://api.mittera.eu/api/v1/emails with Bearer auth verified end-to-end (smoke + magic-link both 200). inbound webhooks at https://api.briven.tech/mittera-webhook verify X-mittera-Signature: v1=<hex> + X-mittera-Timestamp: <ms> with a 5-minute replay window, dispatch per spec §6, audit-log every event.',
268 },
269 {
270 date: '2026-05-11',
271 tags: ['feat'],
272 title: 'nightly backup cron on kvm4',
273 body: 'systemd timer fires /usr/local/bin/briven-backup.sh daily at 02:17 UTC. pg_dump --format=custom against briven-postgres for both briven_control + briven_data, 30-day local retention, off-site upload gated on /etc/briven/backup.env (BRIVEN_BACKUP_S3_*). off-site disabled until B2/R2 creds land.',
274 },
275 {
276 date: '2026-05-11',
277 tags: ['feat'],
278 title: 'briven init --template={blank,todo-app,chat}',
279 body: 'cli init now scaffolds from one of three inline templates. blank = minimal notes; todo-app = 4 mutations + 1 reactive query; chat = two-table per-room reactive. templates are embedded so init works on a fresh machine with no network.',
280 },
281 {
282 date: '2026-05-11',
283 tags: ['feat'],
284 title: 'briven doctor + GET /info build identity',
285 body: 'doctor now pings /info (new endpoint) and reports build sha + build timestamp + uptime alongside the existing health / ready / auth checks. Dockerfile passes BRIVEN_BUILD_SHA + BRIVEN_BUILD_AT through as ARGs for compose to inject.',
286 },
287 {
288 date: '2026-05-11',
289 tags: ['feat'],
290 title: 'admin: email events + suppressions dashboards',
291 body: 'two new pages under /dashboard/admin — email-events (last 200 webhook deliveries with severity-tinted chips) and email-suppressions (recipients we won\'t send to, with manual add / remove). both gated on is_admin.',
292 },
293 {
294 date: '2026-05-11',
295 tags: ['infra'],
296 title: 'forgejo actions ci',
297 body: '.forgejo/workflows/ci.yml runs pnpm -r lint + typecheck + test on every push to main. real eslint config replaces 13 lint stubs; 15 packages green workspace-wide.',
298 },
299 {
300 date: '2026-05-11',
301 tags: ['chore'],
302 title: 'briven.cloud → briven.tech sweep (78 files)',
303 body: 'every public-facing reference to the placeholder briven.cloud domain replaced with briven.tech. scripts/swap-domain.sh ships as the rename helper for future cutovers.',
304 },
305 {
306 date: '2026-05-10',
307 tags: ['feat'],
308 title: '@briven/svelte + @briven/vue clients',
309 body: 'svelte stores (query / mutation, reference-counted by svelte\'s store contract) and vue 3 composables (useQuery / useMutation, onScopeDispose-cleaned). same shape as the react hooks; same setBrivenClient bootstrap.',
310 },
311 {
312 date: '2026-05-10',
313 tags: ['feat'],
314 title: 'briven export / briven import',
315 body: 'briven export writes a project\'s schema + functions to a json archive; briven import reads the archive back into a target project as a deployment. data movement (pg_dump streaming) follows in a later slice.',
316 },
317 {
318 date: '2026-05-10',
319 tags: ['feat'],
320 title: 'briven projects (list + set-default)',
321 body: 'list projects authenticated on this machine; set the local default that other commands fall back to when there\'s no briven.json. zero server round-trip — works against the per-project api keys the cli already stores.',
322 },
323 {
324 date: '2026-05-10',
325 tags: ['feat'],
326 title: 'studio (read mode)',
327 body: 'embedded data browser at /dashboard/projects/:id/studio. table list with approx row counts and storage size; per-table view with column metadata and paginated rows. admin-tier gated; identifier escaping prevents cross-schema reads.',
328 },
329 {
330 date: '2026-05-10',
331 tags: ['infra'],
332 title: 'observability stack + metrics endpoints',
333 body: 'grafana / loki / prometheus / promtail compose ships under infra/observability with four starter dashboards (api requests, runtime invocations, realtime subs, postgres health). /metrics now live on api + realtime; postgres-exporter sidecar template ready for the data plane.',
334 },
335 {
336 date: '2026-05-10',
337 tags: ['feat'],
338 title: 'restore drill (monthly cron)',
339 body: 'pulls the latest pg_dump off off-site storage, sha256-verifies, restores into a throwaway db, sanity-counts core tables, drops the db. systemd timer on the 1st at 04:30 UTC. non-zero exit fires the discord webhook.',
340 },
341 {
342 date: '2026-05-10',
343 tags: ['feat'],
344 title: 'public status page',
345 body: 'docs.briven.tech/status — live probes against api / runtime / realtime, red/green per service, latency + http status, no cache. dns cutover to status.briven.tech is a future ops move.',
346 },
347 {
348 date: '2026-05-10',
349 tags: ['feat'],
350 title: 'open-signups env flag',
351 body: 'BRIVEN_OPEN_SIGNUPS toggles invite-only beta vs. public signups across all three Better Auth paths (email+password, github oauth, magic link). cutover is now config-only, no code change.',
352 },
353 {
354 date: '2026-05-10',
355 tags: ['docs'],
356 title: 'sla matrix on /dashboard/billing',
357 body: 'uptime, response target, support response, rollback window — per tier. free best-effort; pro 99.5% / p99 < 500ms invoke; team 99.9% / p99 < 200ms invoke + 100ms RT fan-out.',
358 },
359 {
360 date: '2026-05-10',
361 tags: ['chore'],
362 title: 'metrics module promoted to @briven/shared',
363 body: 'createMetricsRegistry({help}) factory in @briven/shared/observability replaces three duplicated hand-rolled prometheus exposition modules in api / runtime / realtime. each app instantiates its own scoped registry; ~400 LOC of duplication retired.',
364 },
365 {
366 date: '2026-05-10',
367 tags: ['feat', 'docs'],
368 title: 'public migration guide + public changelog',
369 body: 'docs.briven.tech/migration documents the five principles + ten-step playbook that every briven migration follows, with per-source teasers for convex / supabase / raw-postgres / firebase. this changelog ships alongside.',
370 },
371 {
372 date: '2026-05-10',
373 tags: ['feat'],
374 title: 'phase 3 abuse-report pipeline',
375 body: 'public POST /v1/abuse-reports (anonymous, rate-limited 5/min/IP via cf-connecting-ip) + admin GET/PATCH /v1/admin/abuse-reports for triage. severity (spam / phishing / malware / csam / tos / other) and resolution (no_action / warned / suspended / banned) frozen.',
376 },
377 {
378 date: '2026-05-10',
379 tags: ['feat'],
380 title: 'phase 3 beta-invite dashboard surface',
381 body: 'recipients can now see and accept pending invitations from /dashboard/invitations without going through the email link. the email-link flow stays intact for not-yet-signed-in recipients. accept-by-id replaces the token-based path inside the dashboard so the listing API never has to expose one-time tokens.',
382 },
383 {
384 date: '2026-05-10',
385 tags: ['feat'],
386 title: 'phase 3 usage-metering (invocations slice)',
387 body: 'GET /v1/projects/:id/usage returns invocation count + total duration for the current calendar month UTC, with optional ?from=&until= for custom windows. backed by aggregation over function_logs (no new event table needed for this slice).',
388 },
389 {
390 date: '2026-05-10',
391 tags: ['feat'],
392 title: 'tier-aware rate limits at the gateway',
393 body: 'rateLimit middleware now accepts a dynamic limit fn so caps can vary per project tier. RATE_LIMITS_BY_TIER table: invoke = 60 / 600 / 6000 per minute (free / pro / team), deploy = 5 / 30 / 100. wired onto the invoke and deploy routes; rollout to remaining mutations is the next pull.',
394 },
395 {
396 date: '2026-05-10',
397 tags: ['feat', 'fix'],
398 title: 'briven invoke command',
399 body: 'briven invoke <function> [--body <json>] [--body-file <path>] [--raw] is in the CLI. closes the dogfood smoke-test gap that previously required curl. --raw mode prints the unwrapped function value, jq-pipeable.',
400 },
401 {
402 date: '2026-05-09',
403 tags: ['feat'],
404 title: 'shared structured logger with redaction',
405 body: '@briven/shared/observability exports createLogger(). every emitted line runs through the redaction pass — emails and IPv4 addresses can\'t leak into Loki even if a caller forgets to scrub at the call site. apps/api, apps/runtime, apps/realtime all delegate.',
406 },
407 {
408 date: '2026-05-09',
409 tags: ['security'],
410 title: 'constant-time shared-secret comparison',
411 body: 'the runtime shared secret is now compared with crypto.timingSafeEqual across apps/api/src/routes/internal.ts, apps/runtime/src/index.ts, and apps/realtime/src/index.ts. a remote attacker measuring response latency can no longer recover the secret byte-by-byte.',
412 },
413 {
414 date: '2026-05-09',
415 tags: ['security'],
416 title: 'rate-limit pinned to cf-connecting-ip outside dev',
417 body: 'rate-limit middleware now returns 403 origin_direct_rejected when BRIVEN_ENV !== development and cf-connecting-ip is missing. prevents bypass via direct origin hits (cloudflare-only ingress in production).',
418 },
419 {
420 date: '2026-05-09',
421 tags: ['security'],
422 title: 'BRIVEN_ENCRYPTION_KEY required at boot in non-dev',
423 body: 'previously failed-closed at request time when missing — a deploy could boot, only blowing up when the first customer read an encrypted env var. now fails loud at startup.',
424 },
425 {
426 date: '2026-05-09',
427 tags: ['feat'],
428 title: 'realtime LISTEN/NOTIFY pipeline complete',
429 body: 'apps/realtime now closes the loop: WS subscribe → LISTEN on per-table channels → NOTIFY → re-invoke → push fresh data. internal invoke route added on apps/api so realtime can use the runtime shared secret. fireChannel snapshot iteration prevents touchedTables drift from skipping subscribers mid-fan-out.',
430 },
431 {
432 date: '2026-04-27',
433 tags: ['security'],
434 title: 'owner-tier scaffolding pinned (PR #20)',
435 body: 'reservation for future destructive routes (project delete, member removal). enforced via the project-auth chain.',
436 },
437 {
438 date: '2026-04-27',
439 tags: ['security'],
440 title: 'per-key role scoping for API keys (PR #19)',
441 body: 'human users can issue keys at their own role or lower (viewer / developer / admin). owner-tier is reserved for human owners and never assignable to a key.',
442 },
443 {
444 date: '2026-04-26',
445 tags: ['security'],
446 title: 'HTTPS-origin invariant fail-fast outside dev (PR #18)',
447 body: 'BRIVEN_API_ORIGIN and BRIVEN_WEB_ORIGIN must start with https:// when BRIVEN_ENV !== development. catches misconfigured prod deploys at boot.',
448 },
449 {
450 date: '2026-04-26',
451 tags: ['fix'],
452 title: 'realtime + runtime smoke tests (PR #17)',
453 body: 'bun-test smoke files so CI exits 0 with empty suites. closed the noise from CI runs that warned "no tests found".',
454 },
455 {
456 date: '2026-04-25',
457 tags: ['security'],
458 title: 'org-vs-project authz reconciliation (PR #16)',
459 body: 'effective-role gates resolve project-level role from org membership when the project doesn\'t carry an explicit member row. fixes a class of bugs where org owners couldn\'t access their own projects.',
460 },
461 {
462 date: '2026-04-25',
463 tags: ['security'],
464 title: 'cross-site CSRF closed on /v1/* state-changing routes (PR #15)',
465 body: 'sameSite=strict on the session cookie + Origin-check middleware. unsafe-method requests with a session cookie now require an allow-listed Origin.',
466 },
467 {
468 date: '2026-04-25',
469 tags: ['security'],
470 title: 'schemaSnapshot validator + bound migration insert (PR #14)',
471 body: 'closed a high-severity SQL-injection vector — the schema snapshot is now strictly validated before any DDL is interpolated.',
472 },
473 {
474 date: '2026-04-25',
475 tags: ['security'],
476 title: 'security hardening phase-0 (PR #13)',
477 body: 'better-auth secret rotation, audit-log IP pepper, IP redaction in logs, realtime WebSocket upgrade gate.',
478 },
479 {
480 date: '2026-04-24',
481 tags: ['feat'],
482 title: '@briven/cli ships as a self-contained tarball (PR #10)',
483 body: 'tsup bundles the cli with @briven/schema, @briven/shared, @briven/config inlined. consumers install via pnpm add -D @briven/cli without the workspace-ref problem the prior file: install hit. exposes @briven/cli/schema and @briven/cli/server sub-imports.',
484 },
485 {
486 date: '2026-04-24',
487 tags: ['feat'],
488 title: 'briven link writes projectId into briven.json (PR #11)',
489 body: 'briven link --project <p_…> records the project id locally so subsequent commands (deploy, invoke, env, db) infer it without --project flags.',
490 },
491 {
492 date: '2026-04-24',
493 tags: ['infra'],
494 title: 'wildcard TLS via Cloudflare DNS-01 (PR #9)',
495 body: 'traefik issues *.apps.briven.tech certificates via the cloudflare DNS challenge. each customer project gets its own routable subdomain at deploy time.',
496 },
497 {
498 date: '2026-04-23',
499 tags: ['fix'],
500 title: 'auto-create personal org on signup + lazy backfill (PR #12)',
501 body: 'every user has exactly one personal org from the moment they sign in. /v1/me lazily backfills for older users who pre-date the change.',
502 },
503];