Skip to content

Commit 259fcfe

Browse files
docs: update project docs, add license, and document namespace design
Add Elastic License 2.0 (ELv2). Document attribute namespace design (flat in expressions, nested in API) across permission-system docs and proxy/admin-ui CLAUDE.md files. Update roadmap to replace frontend architecture plan with Catalyst adoption strategy. Add general rules (no hardcoded secrets, TDD, test suite) and conventional commit format to project instructions.
1 parent f107f09 commit 259fcfe

7 files changed

Lines changed: 152 additions & 28 deletions

File tree

.claude/CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ git config core.hooksPath .githooks
3232

3333
Use `/release` to prepare the changelog, bump versions, commit, and tag. Use `/commit` for day-to-day commits.
3434

35+
## General Rules
36+
- **Never hardcode secrets** — API keys, passwords, credentials, and tokens must come from environment variables or encrypted config, never literals in source code.
37+
- **Run the full test suite before considering a task complete**`cargo test` for proxy, `npm run test:run` for admin-ui. Don't declare done until tests pass.
38+
- **TDD for all new code** — write failing tests first, then implement until they pass. This applies to new features and bug fixes alike, not just the bug fix protocol.
39+
3540
## Planning & Feature Design
3641

3742
### Design-First, Discuss Before Building

.claude/commands/commit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,9 @@ description: Create a git commit
1414

1515
Based on the above changes, create a single git commit.
1616

17+
Use conventional commit format: `type(scope): description`
18+
- Types: feat, fix, refactor, test, docs, chore, ci, perf, style
19+
- Scope is optional but preferred (e.g., `proxy`, `admin-ui`, `migration`)
20+
- Description should be lowercase, imperative, no period
21+
1722
You have the capability to call multiple tools in a single response. Stage and create the commit using a single message. Do not use any other tools or do anything else. Do not send any other text or messages besides these tool calls.

LICENSE

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
Elastic License 2.0 (ELv2)
2+
3+
## Acceptance
4+
5+
By using the software, you agree to all of the terms and conditions below.
6+
7+
## Copyright License
8+
9+
The licensor grants you a non-exclusive, royalty-free, worldwide,
10+
non-sublicensable, non-transferable license to use, copy, distribute, make
11+
available, and prepare derivative works of the software, in each case subject
12+
to the limitations and conditions below.
13+
14+
## Limitations
15+
16+
You may not provide the software to third parties as a hosted or managed
17+
service, where the service provides users with access to any substantial set
18+
of the features or functionality of the software.
19+
20+
You may not move, change, disable, or circumvent the license key functionality
21+
in the software, and you may not remove or obscure any functionality in the
22+
software that is protected by the license key.
23+
24+
You may not alter, remove, or obscure any licensing, copyright, or other
25+
notices of the licensor in the software. Any use of the licensor's trademarks
26+
is subject to applicable law.
27+
28+
## Patents
29+
30+
The licensor grants you a license, under any patent claims the licensor can
31+
license, or becomes able to license, to make, have made, use, sell, offer for
32+
sale, import and have imported the software, in each case subject to the
33+
limitations and conditions in this license. This license does not cover any
34+
patent claims that you cause to be infringed by modifications or additions to
35+
the software. If you or your company make any written claim that the software
36+
infringes or contributes to infringement of any patent, your patent license
37+
for the software granted under these terms ends immediately. If your company
38+
makes such a claim, your patent license ends immediately for work on behalf
39+
of your company.
40+
41+
## Notices
42+
43+
You must ensure that anyone who gets a copy of any part of the software from
44+
you also gets a copy of these terms.
45+
46+
If you modify the software, you must include in any modified copies of the
47+
software prominent notices stating that you have modified the software.
48+
49+
## No Other Rights
50+
51+
These terms do not imply any licenses other than those expressly granted in
52+
these terms.
53+
54+
## Termination
55+
56+
If you use the software in violation of these terms, such use is not licensed,
57+
and your licenses will automatically terminate. If the licensor provides you
58+
with a notice of your violation, and you cease all violation of this license
59+
no later than 30 days after you receive that notice, your licenses will be
60+
reinstated retroactively. However, if you violate these terms after such
61+
reinstatement, any additional violation of these terms will cause your licenses
62+
to terminate automatically and permanently.
63+
64+
## No Liability
65+
66+
*As far as the law allows, the software comes as is, without any warranty or
67+
condition, and the licensor will not be liable to you for any damages arising
68+
out of these terms or the use or nature of the software, under any kind of
69+
legal claim.*
70+
71+
## Definitions
72+
73+
The **licensor** is the entity offering these terms, and the **software** is
74+
the software the licensor makes available under these terms, including any
75+
portion of it.
76+
77+
**you** refers to the individual or entity agreeing to these terms.
78+
79+
**your company** is any legal entity, sole proprietorship, or other kind of
80+
organization that you work for, plus all organizations that have control over,
81+
are under the control of, or are under common control with that organization.
82+
**control** means ownership of substantially all the assets of an entity, or
83+
the power to direct its management and policies by vote, contract, or
84+
otherwise. Control can be direct or indirect.
85+
86+
**your licenses** are all the licenses granted to you for the software under
87+
these terms.
88+
89+
**use** means anything you do with the software requiring one of your licenses.
90+
91+
**trademark** means trademarks, service marks, and similar rights.

admin-ui/CLAUDE.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,21 @@ React 19, Vite 7, Tailwind 4, TanStack Query 5, react-router-dom 7, Vitest 4, @t
3333
- `src/pages/AttributeDefinitionEditPage.tsx` — Create/edit attribute definitions (exports `AttributeDefinitionCreatePage` and `AttributeDefinitionEditPage`). Uses `AttributeDefinitionForm` component with standard card wrapper + back button layout.
3434
- `src/components/AttributeDefinitionForm.tsx` — Reusable form for attribute definition create/edit (matches `RoleForm`/`DataSourceForm` pattern). Supports value types: string, integer, boolean, list.
3535
- `src/components/UserAttributeEditor.tsx` — Inline editor for user attributes on UserEditPage. Loads attribute definitions to show type-appropriate inputs (text, number, boolean toggle, enum dropdown, tag/chip input for lists, multi-select checkboxes for lists with allowed values). Shows `{user.KEY}` syntax hint per attribute.
36+
- `src/components/ExpressionEditor.tsx` — CodeMirror-based expression editor for filter/mask expressions
37+
- `src/components/PolicyCodeView.tsx` — Read-only policy code preview
38+
- `src/components/UserAssignmentPanel.tsx` — User-scoped policy assignment panel
39+
- `src/components/UserForm.tsx` — Reusable user create/edit form
40+
- `src/components/Layout.tsx` — App shell layout with sidebar navigation
41+
- `src/pages/PoliciesListPage.tsx` — Paginated policy list
42+
- `src/pages/PolicyCreatePage.tsx` — Policy creation page
43+
- `src/pages/PolicyEditPage.tsx` — Policy edit page with form + assignment panel
44+
- `src/pages/QueryAuditPage.tsx` — Query audit log viewer
3645
- `src/test/test-utils.tsx``renderWithProviders` (QueryClient + AuthProvider + MemoryRouter)
3746
- `src/test/factories.ts``makeUser`, `makeDataSource`, `makeDataSourceType`, `makeDiscoveredSchema/Table/Column`, `makeDecisionFunction`, `makePolicy`, `makePolicyAssignment`
3847

39-
## Running Tests
48+
## Running
4049
```bash
50+
npm run dev # start dev server (proxies /api → localhost:5435)
4151
npm run test:run # single run (CI)
4252
npm test # watch mode
4353
```

docs/permission-system.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,25 @@ Reserved keys for `entity_type = "user"`: `tenant`, `username`, `id`, `user_id`
317317
- An empty list `[]` expands to a single `NULL` literal: `department IN (NULL)` → evaluates to false (no rows match).
318318
- In decision function context, list attributes appear as JSON arrays: `ctx.session.user.departments``["engineering", "security"]`.
319319

320+
### Namespace design: flat in expressions, nested in API
321+
322+
User attributes live at two different levels depending on the context:
323+
324+
| Surface | How attributes appear | Example |
325+
|---|---|---|
326+
| **API payloads** (create/update/response) | Nested under `attributes` | `{ "attributes": { "region": "us-east" } }` |
327+
| **Template variables** (filter/mask expressions) | Flat under `user` | `{user.region}` |
328+
| **Decision function context** | Flat under `user` | `ctx.session.user.region` |
329+
| **CLI config (future YAML)** | Nested under `attributes` | `attributes: { region: us-east }` |
330+
331+
This is intentional:
332+
333+
- **API/storage nests** because attributes are a distinct concern from built-in fields (`username`, `tenant`, `is_admin`). They have different validation rules, full-replace semantics, and are governed by attribute definitions. Nesting makes the API self-documenting about what is user-defined vs. built-in.
334+
- **Expressions/context flatten** because policy authors write these constantly and brevity matters. `{user.region}` and `ctx.session.user.region` are cleaner than `{user.attributes.region}`.
335+
- **Reserved key validation** prevents collisions — attribute keys cannot shadow built-in fields, and built-in fields always take priority at runtime.
336+
337+
The rule is simple: **define attributes under `attributes`, reference them as `{user.KEY}`**.
338+
320339
### Setting attributes on users
321340

322341
User attributes are stored as a JSON column on the user record. They are set via `PUT /api/v1/users/{id}` with an `attributes` field:
@@ -450,7 +469,7 @@ function evaluate(ctx, config) {
450469

451470
`time.now` is an ISO 8601 / RFC 3339 timestamp representing the **evaluation time** — the moment the context is built. For visibility-level functions this is when the connection context is computed; for query-level functions it is when the query is processed. This enables time-windowed decision functions (e.g., break-glass temporary access).
452471

453-
Custom user attributes are flattened as first-class fields on the `user` object with correctly typed values (string/number/boolean/array). List attributes appear as JSON arrays of strings. Built-in fields (`id`, `username`, `tenant`, `roles`) always take priority. See [User Attributes (ABAC)](#user-attributes-abac) for details.
472+
Custom user attributes are flattened as first-class fields on the `user` object with correctly typed values (string/number/boolean/array) — e.g., `ctx.session.user.region`, not `ctx.session.user.attributes.region`. This matches the flat `{user.KEY}` namespace used in template variables. In the API, the same attributes are nested under `attributes` (see [Namespace design](#namespace-design-flat-in-expressions-nested-in-api)). List attributes appear as JSON arrays of strings. Built-in fields (`id`, `username`, `tenant`, `roles`) always take priority. See [User Attributes (ABAC)](#user-attributes-abac) for details.
454473

455474
**Visibility-level enforcement**: `column_deny`, `table_deny`, and `column_allow` policies are enforced at connect time (visibility level) by removing columns/tables from the per-user schema. Decision functions on these policy types are evaluated at visibility time when `evaluate_context = "session"`. If the decision function returns `fire: false`, the policy is skipped and the column/table remains visible. For `evaluate_context = "query"`, the policy's visibility effect is skipped entirely (deferred to query time), since query metadata is not available at connect time — the column/table stays visible in the schema and the decision function runs at query time as normal.
456475

docs/roadmap.md

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -433,39 +433,31 @@ Given complexity of new policy system (interaction with DataFusion and PostgreSQ
433433
- Proposed: Modify hook to only check staged changes using `git diff --staged` or `git diff --cached`
434434
- This preserves ability to have WIP changes without them interfering with the commit process
435435

436-
## Frontend Architecture Guideline: Future-Proofing UI
436+
## Frontend Architecture: Tailwind Plus Catalyst Adoption
437437

438-
### 1. Decouple Logic from Presentation
438+
### Approach
439439

440-
Pattern: Use Custom Hooks (e.g., useAuth, useCart) to handle all API calls, state management, and business logic.
440+
Adopt Tailwind Plus Catalyst as the UI component foundation. Catalyst provides accessible, well-designed React components built on Headless UI + Tailwind CSS v4. Components are copied into the project as source code — no runtime dependency on Catalyst itself.
441441

442-
Rule: Components should only receive data and functions via props. If we want to swap a "List View" for a "Card View" later, the logic hook remains untouched.
442+
### Setup
443443

444-
### 2. Implement a "Headless" Design System
444+
- **Full kit**: `admin-ui/catalyst-kit/` (gitignored) — complete Catalyst download for reference
445+
- **Active components**: `admin-ui/src/components/ui/` — only components in use, copied from the kit as needed
446+
- **Dependencies**: `@headlessui/react`, `motion`, `clsx`, `@heroicons/react`
447+
- **Router integration**: `src/components/ui/link.tsx` — wraps `react-router-dom` `Link` for Catalyst compatibility
448+
- **Docs**: https://catalyst.tailwindui.com/docs/{component-name}
445449

446-
Tooling: Use CVA (Class Variance Authority) to manage Tailwind variants.
450+
### Migration Strategy
447451

448-
Rule: Avoid hardcoding "messy" Tailwind strings directly in feature components. Define styles as Variants (e.g., intent: "primary", size: "lg") in a central UI folder.
452+
Incremental, page-by-page — no big-bang rewrite. Swap raw HTML elements for Catalyst components alongside feature work:
453+
1. Core primitives first: Button, Input, Select, Textarea, Checkbox
454+
2. Layout: Sidebar layout, Navbar (replaces current `Layout.tsx`)
455+
3. Data display: Table, Badge, Description list, Dialog
456+
4. Forms: Fieldset, Radio, Switch, Combobox, Listbox
449457

450-
Goal: Changing the "Look" of the app should involve editing one tailwind.config.js or a few CVA files, not hunting through 50 feature components.
458+
### What This Replaces
451459

452-
### 3. Strict Design Token Usage
453-
454-
Rule: Zero Hex Codes in components. All colors, spacing, and border-radii must come from the tailwind.config.js theme.
455-
456-
Standard: Use semantic naming. Instead of text-blue-600, use text-brand-primary. When v2 arrives, we change the value of brand-primary in one place.
457-
458-
### 4. Component Categorization (Atomic Design)
459-
460-
UI Components (/components/ui): Low-level, stateless elements (Buttons, Inputs, Modals). Use libraries like Radix UI or Headless UI for accessibility logic so we only have to worry about the CSS.
461-
462-
Feature Components (/features/): High-level components that use the UI pieces to solve a business need (e.g., PaymentForm).
463-
464-
### 5. Utility for Class Merging
465-
466-
Requirement: Use a cn() helper function (combining clsx and tailwind-merge).
467-
468-
Benefit: This ensures that if we need to override a "v1" style for a specific edge case, the classes merge correctly without CSS conflicts.
460+
The previous plan (CVA, Atomic Design, strict design tokens, `cn()` helper) is superseded. Catalyst provides variant management, accessible primitives, and consistent design out of the box. No additional abstraction layers needed at the current scale (~25 pages, ~29 components).
469461

470462
## Policy History & Audit Improvements
471463

proxy/CLAUDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pgwire 0.38, DataFusion 52, axum 0.8, SeaORM 1, tokio-postgres 0.7, argon2 0.5,
2525
- `src/entity/attribute_definition.rs` — SeaORM entity for the `attribute_definition` table (id, key, entity_type, display_name, value_type, default_value, allowed_values, description). Includes `validate_value()` and `parse_allowed_values()`.
2626
- `src/admin/attribute_definition_handlers.rs` — CRUD endpoints for attribute definitions (`GET/POST /attribute-definitions`, `GET/PUT/DELETE /attribute-definitions/{id}`). Supports `?entity_type=user` filter and `?force=true` cascade delete via database-specific JSON operations (SQLite `json_remove()`, PostgreSQL `jsonb -`). Includes `validate_json_path_key()` defense-in-depth before SQL interpolation. Update handler invalidates caches when `value_type` changes.
2727
- `src/crypto.rs` — AES-256-GCM `encrypt_json` / `decrypt_json`
28-
- `../migration/src/lib.rs``Migrator` (54 migrations)
28+
- `../migration/src/lib.rs``Migrator`
2929

3030
## Critical Gotchas
3131

@@ -132,6 +132,8 @@ Custom key-value attributes on users, governed by a schema-first attribute defin
132132

133133
**Value types**: `"string"` (→ Utf8 literal), `"integer"` (→ Int64), `"boolean"` (→ Boolean), `"list"` (→ list of strings, max 100 elements). Type-safe substitution in both template variables and decision function context. List attributes expand into multiple comma-separated string literals in `mangle_vars()` for use with `IN` clauses: `department IN ({user.departments})`. Empty lists expand to a NULL sentinel (effectively false). `parse_attributes()` returns `HashMap<String, serde_json::Value>` (not `HashMap<String, String>`).
134134

135+
**Namespace design**: Attributes are nested under `attributes` in the API (`PUT /users/{id}` payload and response) but flat in expressions (`{user.KEY}`) and decision context (`ctx.session.user.KEY`). This is intentional — API nesting separates user-defined from built-in fields; expression flattening keeps policy authoring concise. Reserved key validation prevents collisions. See `docs/permission-system.md` § "Namespace design" for the full rationale.
136+
135137
**Template variables**: `{user.KEY}` in filter/mask expressions. Built-in fields (`{user.tenant}`, `{user.username}`, `{user.id}`) take priority via `match` arms in `UserVars::get()`, preventing attribute override attacks. Custom attributes fall through to the attribute map.
136138

137139
**Decision function context**: Custom attributes are flattened as first-class fields on `ctx.session.user` (e.g., `ctx.session.user.region`) with typed JSON values. Built-in fields (`id`, `username`, `tenant`, `roles`) always take priority. `ctx.session.time.now` is an ISO 8601 / RFC 3339 timestamp of the evaluation time (not session start).

0 commit comments

Comments
 (0)