entries.ts503 lines · main
| 1 | export interface ChangelogEntry { |
| 2 | date: string; // ISO yyyy-mm-dd |
| 3 | tags: readonly ChangelogTag[]; |
| 4 | title: string; |
| 5 | body: string; |
| 6 | } |
| 7 | |
| 8 | export type ChangelogTag = 'feat' | 'fix' | 'security' | 'docs' | 'infra' | 'chore'; |
| 9 | |
| 10 | export 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 | ]; |