Skip to content

Latest commit

 

History

History
424 lines (319 loc) · 39.5 KB

File metadata and controls

424 lines (319 loc) · 39.5 KB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.17.3] - 2026-04-26

Changed

  • [Both] Drop client_ip from audit log — the recorded value was the Fly edge proxy address, not the real client, so it was misleading. The field is removed from the admin API response (AuditLogResponse) and the React audit page; the proxy stops writing it (always None) on both the deny path and the query-completed path.
    • DB column kept for backward compatibility but no longer populated
    • Public docs (guides/audit-debugging.md) updated

[0.17.2] - 2026-04-24

Changed

  • [Infrastructure] Bump Fly.io VM to shared-cpu-2x / 512 MB — doubles memory headroom and burst CPU credits. The previous shared-cpu-1x / 256 MB sized warm-idle around 150 MB, leaving ~50 MB of headroom that a single active session could exhaust under non-pushdown DataFusion execution (cross-datasource joins, json_length / json_keys UDFs). The spike-then-throttle pattern under heavy queries is reduced (longer burst window before kernel CFS throttling kicks in) but not eliminated — moving to performance-1x would be required to remove burst-credit throttling outright.

Docs

  • [Docs] README points at unversioned screenshot filenames — image links no longer go stale across releases.

[0.17.1] - 2026-04-24

Changed

  • [Admin UI] Anchor-coverage warning is harder to miss and easier to act onPolicyEditPage now hoists the useQuery(['policy-anchor-coverage']) to the page level so the side-nav and the panel share one cache entry. The Anchor coverage section in the secondary nav now carries a red count pill (SectionDef.indicator) when the policy will silently deny on any table; a top-of-page red banner ("This row filter will silently deny on N tables") with a Review button appears on every section except the coverage section itself, and inherits the active section's max-width so it lines up with the form/content below. The coverage panel itself was redesigned: each broken table is its own card with bold header (data source + schema.table), per-column rows with a clearer reason line, and a tertiary Add anchor → button instead of a plain underline. The outer wrapper switched from red wash to a neutral white card so the per-table red still reads as the alarm without desensitizing.
  • [Admin UI] Consistent schema-alias labeling across relationship/anchor surfaces — new effectiveSchemaName(schema_name, schema_alias) helper in src/utils/schemaLabel.ts is now the single rule used by RelationshipsPanel, PolicyAnchorCoveragePanel, and the ?focus= deep-link matcher: display the alias if set, raw upstream name otherwise. Dropdowns and selection contexts also render the upstream name in muted parens (pg.orders (postgres.orders)) so admins can verify the mapping without bouncing to the discovery wizard. The anchor table's relationship cell now shows the full child_schema.child_table.child_col → parent_schema.parent_table.parent_col join path (the child side was previously missing), and the column header was renamed from Via relationship to Resolves via to honestly cover both the FK-walk and same-table-alias modes.
  • [Admin UI] Anchor creation flow on deep-link from the policy warning — when arriving via the Add anchor → link, the form now auto-selects the relationship if exactly one candidate's parent table contains the resolved column. Multiple viable candidates are sorted first in the dropdown and prefixed with . When the prefilled child table has zero relationships, the form replaces the previous one-line amber sentence with an empty-state guidance block: a "Switch to Same-table alias" button + an inline FkSuggestionsList filtered to the child table + a compact InlineManualRelationshipForm (parent table + columns) that auto-injects the new relationship into the anchor on success. The user no longer has to leave the anchor flow to fix the missing prerequisite.

Added

  • [Proxy] schema_upstream field on the anchor-coverage responseAnchorCoverageTableEntry now carries both schema (effective name, alias if set) and schema_upstream (raw upstream name). The admin UI uses this to render pg.payments (postgres.payments) in the warning panel when an alias is in play, while continuing to deep-link by the effective name (which is what the proxy keys columns by at query time).
  • [Admin UI] SectionDef.indicator slot on SecondaryNav — sections can now carry an optional { tone: 'red' | 'yellow'; label: string; ariaLabel?: string } indicator that renders as a small count pill on the right of the nav button. Currently consumed by PolicyEditPage for anchor coverage; the slot is generic so future sections can adopt it without further changes to the component.

[0.17.0] - 2026-04-23

Added

  • [Admin UI] Design standard and shared layout primitives — new admin-ui/DESIGN.md defines four page templates (T1 sidebar detail, T2 single form, T3 list, T4 audit) plus the header / save / destructive / modal rules they share. Eight new primitives land under src/components/ and src/components/layout/: PageHeader, SecondaryNav + useSectionParam, SectionPane, DangerZone + DangerRow, ModalShell (with focus trap, Esc, click-outside), StatusDot + StatusChip, ConfirmDeleteModal, and ConfirmDialog. Each ships with unit tests. admin-ui/CLAUDE.md gained a "Design standard" pointer so future sessions pick up the rules before building a new page.
  • [Admin UI] Every edit page now uses the sidebar template — Policy, Role, User, and Attribute Definition edit pages all adopted the shared T1 shape pioneered by DataSource, so the admin UI shows a single consistent visual language across resources. Section layouts vary by content: Policy has Details / Assignments / Anchor coverage (row_filter only) / View as code / Activity; Role consolidated its former six tabs into Details / Membership (direct members + parent/child inheritance) / Access grants (datasource access + policy assignments) / Activity; User has Profile / Password / Activity; Attribute Definition has Details / Activity. Save shows a toast and stays on the page (previously Policy, Role, and User each had different auto-navigate quirks). All edit pages carry a danger zone with typed-name delete through the shared ConfirmDeleteModal; soft-delete escapes are "Disable instead" for policies (is_enabled), "Deactivate instead" for roles and users (is_active), and a two-step force-delete for attribute definitions that orphan user values.
  • [Proxy] Transitive column resolution for row filters — row-filter policies can now reference columns that aren't on the matched table, resolved via admin-curated table_relationship + column_anchor catalog. Anchors come in two XOR-exclusive shapes: (a) FK walk (relationship_id), where the rewriter injects an InnerJoin to a parent table that carries the column, up to a hardcoded depth of 3; (b) Same-table alias (actual_column_name), where the rewriter substitutes the column name inside the filter expression with no join — covers the case where one broad policy targets tables whose tenant-isolation column is spelled differently (tenant_id on one table, org_id on another). Resolution failures (missing anchor, depth > 3, cycle, qualified parent ref, or column missing from scan schema — including alias anchors pointing at a non-existent column) substitute Filter(lit(false)) with a column_resolution_unresolved warn log. See docs/security-vectors.md vector 73 for the threat model and docs/permission-system.md for the operator-facing description.
  • [Proxy] Admin REST endpoints for relationships and column anchorsGET/POST /datasources/{id}/relationships, DELETE /datasources/{id}/relationships/{rel_id}, GET/POST /datasources/{id}/column-anchors, DELETE /datasources/{id}/column-anchors/{anchor_id}, and GET /datasources/{id}/fk-suggestions (live pg_constraint introspection filtered to single-column FKs whose parent column is a PK or single-column unique). All mutations go through AuditedTxn and are tagged on the admin_audit_log. The XOR between relationship_id and actual_column_name is enforced server-side with a clear 422.
  • [Admin UI] "Relationships & column anchors" panel on the datasource edit page — new RelationshipsPanel.tsx lets admins promote live FK suggestions or add relationships manually, and designate column anchors with a "Resolve via" radio for either the FK-walk shape (relationship dropdown scoped to the selected child table) or the same-table alias shape (column name with a datalist of the table's own columns). Long child → parent labels stack onto two lines so narrow-container layouts don't clip the action column.
  • [Proxy] Anchor-coverage preview endpointGET /policies/{id}/anchor-coverage dry-runs the same column-resolution logic the runtime uses. For every (assigned table × column referenced in the row-filter expression) it returns a verdict: on_table, anchor_walk (with FK edge metadata), anchor_alias, missing_anchor (the silent-deny case), or missing_column_on_alias_target. Reuses RelationshipSnapshot and expr_column_names; no caching needed (sub-millisecond per table after the one-time parse). Returns empty coverage for non-row-filter policy types.
  • [Admin UI] Anchor-coverage warning on the policy edit page — new PolicyAnchorCoveragePanel.tsx renders on PolicyEditPage for row_filter policies. Green banner when every assigned-table × referenced-column pair resolves; red panel listing each broken pair with a deep link to the datasource page where the missing anchor can be added. Catches the silent-deny failure mode (filter references a column that isn't on the target table and has no anchor → runtime substitutes Filter(false)) at edit time instead of when users notice empty result sets. Refetches automatically after save via the existing policy.version invalidation; hidden for non-row-filter policy types.

Changed

  • [Admin UI] Destructive row actions removed from list pages; attribute-definition delete ported to the edit pageUsersListPage, RolesListPage, PoliciesListPage, and AttributeDefinitionsPage no longer show a per-row Delete button. Deletion now happens on each resource's edit page through the danger-zone flow (typed-name confirmation + soft-delete escape). The attribute-definition page's custom "force delete on FK-constraint error" flow was ported to the edit page's danger-zone modal: a first Delete attempt may fail with a force-required error, and the button then re-labels itself "Force delete" for a second click. Inline relationship + column-anchor deletes in RelationshipsPanel use the new ConfirmDialog instead of native confirm().
  • [Admin UI] Create pages use breadcrumb headers and navigate to the new resource's edit pageDataSourceCreatePage, RoleCreatePage, PolicyCreatePage, and AttributeDefinitionCreatePage replaced the ← Back to X button with a PageHeader breadcrumb matching the detail-page pattern. DataSource and Policy creation now land on /{resource}/{newId}/edit on success instead of the list, so users can immediately continue configuring the thing they just made.
  • [Admin UI] Policy "View as code" is always expanded in its section — the legacy click-to-expand toggle made sense when the code view was one of several stacked cards on a scroll page, but in the new sidebar layout the user has already clicked "View as code" in the secondary nav to reach the section. The second click is pure friction. The YAML/JSON switcher and Copy button now sit in the section header alongside an always-rendered code block.
  • [Admin UI] Sidebar logo and favicon — added a logo.svg next to the "BetweenRows" brand text in the admin sidebar header, and a favicon.svg for the browser tab.
  • [Docs] Public documentation site sidebar restructure — reorganized the VitePress sidebar into five top-level sections (Start, Concepts, Features, Guides, About), with Policy Types, Template Expressions, and Decision Functions nested under Policies. Deleted standalone reference pages (reference/policy-types, reference/audit-log-fields, reference/cli, reference/admin-rest-api, about/changelog, about/license) — content either folded into the guide it documents or replaced with an external link to the canonical source on GitHub (LICENSE, CHANGELOG.md).

[0.16.1] - 2026-04-13

Changed

  • [Docs] Repositioned from "alpha" to "beta" — single canonical page (docs-site/docs/about/license.md, now "License & Beta Status") describes pre-1.0 stability, what's stable vs. less stable, and the recommended posture for early adopters. Scattered "alpha" caveats across README, SECURITY.md, the VitePress sidebar/footer, and a dozen docs pages have been deleted or reframed — substance kept (pin tags, read changelog, file issues), positioning dropped from places that were just repeating the canonical disclaimer. Skipping the alpha→beta transition removes a fuzzy intermediate gate; the next stage is 1.0 with API stability commitments.

Infrastructure

  • [Docs] Switch public documentation site domain to docs.betweenrows.dev — centralize URLs + OG image in docs-site/docs/.vitepress/constants.ts, add a path-gated Cloudflare Pages deploy workflow (.github/workflows/docs-site-deploy.yml), and enable Cloudflare Web Analytics via automatic edge injection. Remove obsolete docs-site/internal/ notes and the commented-out docs-site CI job.
  • [Docs] Tokenize release version across docs pages — markdown files use a {{VERSION}} token substituted at build time from constants.ts via a Vite pre-transform. Covers rendered HTML, the copy-as-markdown .md output, and llms-full.txt. Future releases bump one file instead of ~13.
  • [Docs] README polish and marketing alignment — headline + pillars rewritten to match the www landing page (A fully customizable data access governance layer.), added docs.betweenrows.dev link and a screenshots table linking into docs-site/docs/public/screenshots/, replaced the stale 0.11.0 Docker tag example with a pointer to the Tags page, and routed the permission-system and roadmap cross-references into the public docs. Relaxed the documentation-architecture rule in .claude/CLAUDE.md so README.md, SECURITY.md, and CONTRIBUTING.md may link into docs-site/ for self-contained GitHub rendering (code trees still may not).
  • [Admin UI] "Report an issue" footer link — bumped to the new docs.betweenrows.dev domain.
  • [Repo] .github/ISSUE_TEMPLATE/config.yml — directs security reports to GitHub Security Advisories and questions to Discussions.
  • [Repo] SECURITY.md links — rewritten to reference in-repo files instead of external docs URLs, so the GitHub Security tab is self-contained.

[0.16.0] - 2026-04-13

Added

  • [Docs] Public documentation site — full VitePress site at docs-site/ published to docs.getbetweenrows.com.
    • Installation (Docker, Fly, from source), Start (introduction, quickstart), Concepts (architecture, policy model, security overview, threat model), Guides (policies, users/roles, attributes, data sources, decision functions, audit debugging, recipes), Reference (REST API, config, CLI, policy types, template expressions, audit fields, glossary, demo schema), Operations (backups, upgrading, troubleshooting, known limitations, rename safety), About (roadmap, changelog, license, report an issue).
    • concepts/threat-model.md auto-transcludes docs/security-vectors.md so the public threat model stays in lockstep with the design source.
  • [Docs] SECURITY.md — root-level vulnerability disclosure policy.

Changed

  • [Admin UI] List-page truncation polish — Attributes, Policies, and Roles list tables now truncate long names and descriptions with tooltip titles instead of letting them push the table layout.
  • [Admin UI] "Report an issue" footer link — now points at docs.getbetweenrows.com/about/report-an-issue instead of the GitHub issues page.
  • [Docs] Threat model H1docs/security-vectors.md retitled from "Security Vectors" to "Threat Model" so the transcluded public page has the right heading.

Infrastructure

  • [Both] /docs-sync command — new Claude Code workflow that detects drift between docs/ + source and docs-site/, presents findings for review, and applies approved edits.
    • Diff mode (/docs-sync <range>), full-codebase audit (--full), and single-page audit (--page <path>).
    • Runs automatically as step 2 of /release.
  • [Docs] docs-site/.gitignore**/.vitepress/{dist,cache}/ now matches at any depth to guard against stray builds from the wrong cwd.

[0.15.0] - 2026-04-11

Added

  • [Proxy] Extensive policy enforcement tests for aggregates, HAVING, window functions, CTEs, and subqueries — +1240 lines in policy_enforcement.rs covering how column masks, column denies, and column allows interact with COUNT(DISTINCT), GROUP BY / HAVING, ROW_NUMBER() OVER (ORDER BY ...), CTEs, and subqueries. Ensures masked values cannot leak through aggregates or window ordering.
  • [Docs] Security vectors documentation overhaul — major expansion of docs/security-vectors.md with new attack vectors, defenses, and test back-references; docs/permission-system.md updated in lockstep.
  • [Demo] Ecommerce demo refresh — new compose.demo.yaml, new setup.sh automation script, updated schema.sql and seed.py, refreshed policies.yaml and requirements.txt, and a rewritten README.

Changed

  • [Proxy] BREAKING: ctx.query.tables is now an array of objects, not strings — decision functions with evaluate_context = "query" previously received ctx.query.tables as string[] (e.g. ["public.orders"]). It is now Array<{datasource, schema, table}>, so decision function JS must access the fields explicitly. Bare references like SELECT * FROM orders now also resolve to the session's default schema (e.g. public) rather than an empty schema segment, so qualified and unqualified references produce identical entries.

    Migration for any decision function that inspected ctx.query.tables:

    // Before:
    ctx.query.tables.includes("public.orders")
    ctx.query.tables.some(t => t.startsWith("public."))
    
    // After:
    ctx.query.tables.some(t => t.schema === "public" && t.table === "orders")
    ctx.query.tables.some(t => t.schema === "public")
  • [Admin UI] Form polish — small tweaks to CatalogDiscoveryWizard, DecisionFunctionModal, PolicyForm, and DataSourceEditPage.

Fixed

  • [Proxy] Security: bare table references could bypass schema-scoped policies — unqualified references like FROM orders previously used an empty schema segment as the policy lookup key, so a policy targeting schemas: ["public"] would not match and could be bypassed by omitting the prefix. Bare references now fall back to the session's default schema, which DataFusion is already configured with at connect time (SET search_path is blocked upstream by ReadOnlyHook). Tracked as vector #71 in docs/security-vectors.md.

Infrastructure

  • [CI] Pre-commit hook runs docs-site VitePress build when docs-site changes are staged — guarded by a docs-site/node_modules check so fresh clones without docs deps installed are not blocked.
  • [CI] docs-site GitHub Actions job added then disabled — the job is commented out until docs-site/ lands in the repo.

[0.14.1] - 2026-04-09

Changed

  • [Both] Remove git commit hash from version display — simplifies the build and /health endpoint
    • drops GIT_COMMIT_SHORT env var and git-based build.rs logic from the proxy
    • sidebar now shows vX.Y.Z instead of vX.Y.Z (abc1234)
  • [Admin UI] Polish admin UI tables and forms
    • attribute definitions table: combine display name + description into a single column, show entity.key as a monospaced code chip, and render value type as a type signature (e.g. list<string> ∈ {us, eu})
    • roles list: fold description under role name, drop the standalone description column
    • user form: add a permissions description explaining what admin access grants
  • [Admin UI] Rename audit nav and standardize route paths
    • sidebar section renamed from "Activity" to "Audit"
    • nav labels changed to "Query Logs" / "Admin Logs"
    • /audit route renamed to /query-audit for consistency with /admin-audit

[0.14.0] - 2026-04-08

Added

  • [Both] Entity search, copyable IDs, and audit improvements — server-side search for attribute definitions, entity search dropdowns on audit pages, copyable UUID components across list pages, debounce hook, and new admin/query audit page tests
    • Proxy: search filter on GET /attribute-definitions, copyable IDs in audit responses, policy enforcement test coverage for missing attribute defaults
    • Admin UI: CopyableId component, EntitySelect component, useDebounce hook, admin audit & query audit page tests
  • [Both] Version display — app version and git commit hash shown in sidebar footer
    • Proxy: /api/version endpoint serving version from Cargo.toml + build-time git commit
    • Admin UI: useVersion hook, version display in Layout

Changed

  • [Both] Debounced search and keepPreviousData — replaced form-submit search with real-time debounced search across all list pages (Users, Roles, Policies, Data Sources, Attributes); added keepPreviousData to prevent layout flash during transitions
  • [Admin UI] Sidebar navigation redesign — grouped nav into Access Control / Data / Activity sections with Heroicons; added "Report an issue" link in footer; username prefixed with @
  • [Admin UI] Default value UX improvements — type-specific placeholders, inline NULL badge when empty, icon-based clear button in attribute definition form
  • [Admin UI] Attribute definitions table — added entity type column, reordered entity type filter before search input
  • [Admin UI] Audit timeline ��� reduced page size from 20 to 5 for inline timelines; left-aligned pagination
  • [Admin UI] Table header styling — consistent text-xs sizing across all list page headers
  • [Both] NULL terminology standardized — replaced inconsistent "no default (null)" phrasing with explicit "NULL" across UI, docs, code comments, and security vectors

Fixed

  • [Proxy] Zero-column scan — fixed EmptyProjectionFixRule handling when all columns are denied

[0.13.0] - 2026-04-06

Added

  • Tenant as custom attribute — the built-in tenant column on proxy_user has been removed; tenant is now managed entirely through the ABAC attribute definition system
    • Migration 055 drops the tenant column from proxy_user
    • {user.tenant} template variable still works — resolves from the user's custom attributes
    • BR_ADMIN_TENANT env var removed (was already deprecated)
    • tenant is no longer a reserved attribute key — can be created/deleted like any other custom attribute
    • Admin UI tenant field removed from user forms and list pages

[0.12.0] - 2026-04-06

Added

  • BetweenRows rebrand — renamed from QueryProxy to BetweenRows across CLI, admin UI, Dockerfile, and configuration files
  • Auto-persisted secrets — encryption key and JWT secret now follow a three-tier resolution: env var → persisted file → auto-generate and save
    • Keys are persisted to .betweenrows/ state directory alongside the database, surviving container restarts without explicit env vars
    • Persistence warning on startup if the state directory is missing alongside existing data (likely unmounted volume)
    • Data directory inferred from BR_ADMIN_DATABASE_URL for consistent state file placement
  • Startup banner — displays version and tagline on boot
  • Linux aarch64 support for Javybuild.rs now downloads the correct Javy binary for linux/aarch64 (ARM servers, Graviton, etc.)
  • Docker quickstart composecompose.quickstart.yaml for one-command local setup
  • Governance workflows roadmap — detailed design for three-tier governance (none → draft → code) with sandboxes, YAML-as-code, and CI/CD deployment

Changed

  • README rewritten as user-facing quickstart — streamlined for new users with Docker quick start, 5-minute walkthrough, configuration reference, and policy overview
  • Developer docs moved to CONTRIBUTING.md — architecture details, data model, API reference, and performance notes relocated from README
  • Fly.io deployment docs — extracted to docs/deploy-fly.md
  • SQLx logging suppressed below DEBUGsqlx_logging now only enabled when RUST_LOG includes DEBUG or lower, reducing noise in default info mode
  • Dockerfile sets BR_ADMIN_DATABASE_URL — explicitly sets the SQLite path to /data/proxy_admin.db for consistent data directory detection

Infrastructure

  • .betweenrows/ added to .gitignore — auto-persisted state directory excluded from version control

[0.11.0] - 2026-03-29

Fixed

  • Decision function test context — mock context in the expression editor nested user attributes under an attributes key instead of flattening them as top-level fields on ctx.session.user, causing runtime errors when testing functions that access custom attributes (e.g. ctx.session.user.departments). Added cross-reference comments between context.rs and DecisionFunctionModal.tsx to prevent future drift.

[0.10.0] - 2026-03-29

Changed

  • Flatten user attributes in decision function context — Custom attributes are now first-class fields on ctx.session.user (e.g., ctx.session.user.region) instead of nested under ctx.session.user.attributes. Built-in fields (id, username, tenant, roles) always take priority on collision.
    • BREAKING: Existing decision functions referencing ctx.session.user.attributes.* must be updated to ctx.session.user.*

Added

  • Expression editor with autocomplete — Filter and mask expression fields in PolicyForm now use a CodeMirror editor with {user.*} template variable autocomplete (built-in + custom attribute definitions).
  • Server-side expression validation — New POST /policies/validate-expression endpoint and "Check" button in the expression editor to validate filter/mask syntax before saving.
  • ORM-derived reserved attribute keys — Reserved user attribute keys are now computed from the proxy_user ORM columns (+ virtual fields like roles), preventing accidental collisions with DB-level field names.
  • Conditional policy documentation — Comprehensive ABAC expression patterns and conditional policy examples for all five policy types added to docs/permission-system.md. Conditional Policies marked as resolved in roadmap (covered by CASE WHEN expressions + decision functions).

[0.9.0] - 2026-03-28

Added

  • List attribute type for ABAC user attributes — new "list" value type for attribute definitions, storing arrays of strings (max 100 elements)
    • Use with IN ({user.KEY}) in filter expressions; list expands into comma-separated placeholders
    • Empty lists expand to NULL (effectively returning no rows)
    • API validates list values as JSON arrays of strings; allowed_values constrains individual elements
    • Decision function context includes list attributes as JSON arrays
    • Admin UI: tag/chip input for free-form lists, multi-select checkboxes for lists with allowed values
    • Extracted AttributeDefinitionForm component (matches RoleForm/DataSourceForm pattern)
    • Added PolicyForm validation: blocks submit when decision function toggle is on but no function is attached or reference is stale
    • DecisionFunctionModal: autocomplete hints for per-attribute ctx.session.user.attributes.*, test context pre-populated from current user's real attributes
    • Config JSON validation: blocks save on invalid JSON instead of silently defaulting to {}

[0.8.0] - 2026-03-28

Added

  • User Attributes (ABAC) — schema-first attribute system for attribute-based access control
    • attribute_definition table defines allowed keys with types (string/integer/boolean), entity type scoping, optional enum constraints, and reserved key protection
    • User attribute values stored as JSON column on proxy_user with full-replace semantics and write-time validation
    • Typed {user.KEY} template variables in filter/mask expressions (Utf8/Int64/Boolean literals)
    • User attributes available in decision function context as ctx.session.user.attributes with typed JSON values
    • time.now (RFC 3339 evaluation timestamp) added to decision function context for time-windowed access
    • Admin UI: attribute definition list/create/edit pages, user attribute editor with type-aware inputs
    • CRUD API with ?force=true cascade delete (SQLite json_remove() / PostgreSQL jsonb -)
    • 3 new migrations (052–054)
  • Save-time expression validation — filter and mask expressions are validated at policy create/update time; unsupported SQL syntax returns 422 immediately instead of failing silently at query time
    • CASE WHEN expression support added to the expression parser

Changed

  • Shared WASM runtime — consolidated WasmDecisionRuntime into a single Arc singleton created at startup, shared by PolicyHook, EngineCache, and AdminState (replaces per-use instantiation)
  • Security vectors doc renameddocs/permission-security-tests.mddocs/security-vectors.md; added vectors 59–68 covering predicate probing, aggregate inference, EXPLAIN leakage, HAVING bypass, CASE expression bypass, window function ordering, timing side channels, and ABAC-specific vectors

[0.7.0] - 2026-03-26

Added

  • Decision functions — custom JavaScript functions that control when policies fire, evaluated in a sandboxed WASM runtime
    • Two evaluation contexts: session (evaluated once at connect) and query (evaluated per query)
    • Configurable error handling: skip (policy doesn't fire) or deny (query blocked)
    • Console log capture with configurable log levels
    • CRUD API with test endpoint for dry-running functions against mock contexts
    • Integrated into visibility-level evaluation: column_deny and table_deny policies respect decision function results at connect time
  • Decision function admin UI — modal for creating/editing decision functions with CodeMirror editors
    • JavaScript and JSON editors with ctx.*/config.* autocomplete
    • Templates for common patterns in create mode
    • Test panel with client-side pre-check and server-side WASM execution
    • Fire/skip/error result badges, shared function warning, optimistic concurrency
    • PolicyForm integration: toggle-based attachment (create new / select existing / edit / detach)

Fixed

  • Stale decision function reference dead-end — detaching a deleted function now correctly reveals create/select buttons instead of leaving the user stuck
  • Testcontainers leak — label containers and clean up orphans to prevent Docker resource exhaustion during test runs

[0.6.0] - 2026-03-23

Added

  • RBAC with transactional audit enforcement — role-based access control with AuditedTxn wrapper
    • Roles with hierarchical membership (BFS traversal, cycle detection, depth cap)
    • Policy assignments scoped to user, role, or all
    • AuditedTxn ensures every admin mutation is atomically committed with its audit log entries
    • Role deactivation/reactivation cascades to policy visibility
    • Datasource access gating by role membership

Infrastructure

  • Remove LICENSE from git history — dropped LICENSE commit from history, added LICENSE*/LICENCE* to .dockerignore

[0.5.2] - 2026-03-18

Added

  • Scan-level column masking — Column masks now apply at the TableScan level via transform_up instead of only at the top-level Projection, preventing CTE and subquery nodes from bypassing masks by changing the DFSchema qualifier.
    • Masks run before row filters so filters evaluate against raw (unmasked) data
    • Integration tests for multi-table JOINs with scoped column deny, CTE mask bypass prevention, subquery mask enforcement, and combined mask+deny+filter scenarios

[0.5.1] - 2026-03-17

Changed

  • Dependency upgrades — Rust 1.94, DataFusion 52.3, Vite 7, TypeScript 5.9

Fixed

  • Read-only hook test assertions — use as_db_error() instead of to_string() for reliable error matching
  • Row filter projection expansion — fix CI test failures related to projection expansion and table_deny audit status
  • Unused variable warning — fix compiler warning in catalog_handlers

Infrastructure

  • CI actions upgraded to v5actions/checkout and actions/setup-node updated from v4 to v5 for Node.js 24 support

[0.5.0] - 2026-03-16

Added

  • Flat policy type model — replaced the obligation model with a flat policy_type field
    • 5 types: row_filter, column_mask, column_allow, column_deny, table_deny
    • Removed column_access action field; type alone determines behavior
    • 5 new migrations (019–023) to migrate existing schema
  • Zero-trust column model — qualified projection for JOINs; per-user column visibility enforcement
  • Cast support — SQL type cast expressions now handled in query processing
  • Catalog hints — contextual hints surfaced in the catalog discovery UI
  • Policy-centric assignment panel — rule assignment UI redesigned around policies rather than datasources
  • Datasource assignment on create — assign a datasource when creating a new rule
  • Audit status tracking — queries now record a status field in the audit log
  • Audit write rejections — rejected write queries are now captured in the audit log
  • Container-based integration tests — replaced manual test scripts with a Docker-based test suite

Fixed

  • Column mask and row filter bugs — fixed incorrect mask application and cross-policy row filter interactions
  • Audit duration and rewritten query — fixed these fields not being recorded correctly
  • SPA routing — production build now serves index.html for client-side routes

[0.4.0] - 2026-03-08

Added

  • Policy system — configurable row filtering, column masking, and column access control via named policies assigned to datasources and users
    • policy, policy_version, policy_obligation, policy_assignment, query_audit_log database tables (migration 007)
    • PolicyHook replaces RLSHook; supports row_filter, column_mask, and column_access obligation types
    • Template variables ({user.tenant}, {user.username}, {user.id}) with parse-then-substitute injection safety
    • Wildcard matching (schema: "*", table: "*") in obligation definitions
    • access_mode on datasources: "policy_required" (default) or "open"
    • Optimistic concurrency via version field (409 Conflict on mismatch)
    • Immutable policy_version snapshots on every mutation for audit traceability
    • Deny policies short-circuit with error before plan execution
    • 60-second per-session cache with invalidate_datasource / invalidate_user hooks
  • Policy API — admin-only CRUD and assignment endpoints
    • GET/POST /policies, GET/PUT/DELETE /policies/{id}
    • GET/POST /datasources/{id}/policies, DELETE /datasources/{id}/policies/{assignment_id}
  • Query audit log — async logging of every proxied query
    • GET /audit/queries with pagination and filtering by user, datasource, date range
  • Visibility-follows-access — per-connection, per-user filtered SessionContext
    • Users only see tables and columns their policies permit
    • Policy changes take effect immediately without reconnect
  • JSON/JSONB supportjson and jsonb columns via DataFusion v52 and datafusion-functions-json
    • -> / ->> operators and JSON UDFs available in queries
    • Filter pushdown to upstream PostgreSQL for supported operators
  • EXPLAIN supportEXPLAIN <query> returns a PostgreSQL-compatible single-column QUERY PLAN response
  • Admin UI — Policies — list, create, and edit policies with an obligation builder; inline enable/disable toggle
  • Admin UI — Policy Assignments — assign/remove policies per datasource with optional user scope and priority
  • Admin UI — Query Audit — paginated audit log with original query, rewritten query, and applied policy snapshots
  • Demo e-commerce schemascripts/demo_ecommerce/ with schema, seed script, and example policies
  • Docsdocs/permission-system.md (user guide) and docs/security-vectors.md (security test plan)

Changed

  • Arrow encoding — migrated to arrow-pg; handler simplified; removed arrow_conversion and sql_rewrite modules

Infrastructure

  • CI/CD — split into CI (tests on every push to main) and CD (publish + deploy on v* tag only)
    • Docker images tagged X.Y.Z and X.Y; deploy uses explicit version tag for prod traceability
    • workflow_dispatch added for manual redeployment of an existing version

[0.3.0] - 2026-03-04

Added

  • Password toggle visibility on login/password fields
  • Password complexity validation
  • Catalog viewer page for browsing the discovered catalog
  • Button on the data source list view to open the catalog viewer

Fixed

  • tsc -b typecheck failure in client.test.ts; aligned typecheck script accordingly

[0.2.2] - 2026-03-04

Fixed

  • TypeScript errors in test files (as unknown as casts, unused imports) that were silently ignored by Vitest/esbuild but caught by tsc during Docker build

Infrastructure

  • Add typecheck script (tsc --noEmit) to admin-ui and run it in the pre-commit hook before tests, so type errors are caught locally before CI

[0.2.1] - 2026-03-04

Added

  • /commit slash command for Claude Code
  • /release skill with semver Docker image tagging

Infrastructure

  • Admin-ui test suite with Vitest, integrated into CI

[0.2.0] - 2026-03-04

✨ Added

  • Multi Data Source Management: The proxy now supports connecting to multiple, dynamically configured upstream data sources.
  • Data Source Admin API & UI: New endpoints and UI pages for creating, editing, and testing data source configurations.
  • User-to-Data Source Access Control: Implemented a many-to-many permission model to assign users to specific data sources.
  • Encryption at Rest: Sensitive data source configuration fields (e.g., passwords) are now encrypted with AES-256-GCM in the database.
  • Engine Cache: Implemented a cache for DataFusion SessionContexts, one for each active data source, to improve performance and resource management.
  • Structured Logging: Replaced println! with tracing for structured, level-based logging.

♻️ Changed

  • Authentication Flow: The PostgreSQL database parameter in the connection string is now used to select the target data source.
  • Project Version: Incremented crate versions to 0.2.0 to reflect new feature set.
  • Schema Alias Support: Catalog discovery now supports schema aliases for more flexible data source mapping.
  • Per-Column Selection: Catalog discovery wizard allows selecting individual columns per table.
  • Idle Connection Timeout: pgwire proxy now closes idle connections after a configurable timeout.
  • Fly.io Auto Stop/Start: Deployment is configured to automatically stop and start machines based on traffic.

[0.1.0] - (Initial Release)

  • Initial implementation of the PostgreSQL wire protocol proxy.
  • Authentication for proxy users via Argon2id password hashing.
  • Basic query processing using the Apache DataFusion engine.
  • Rudimentary admin REST API for user management.
  • Initial Admin UI for listing and creating users.