CLAUDE.md rewritten (708 -> ~320 lines): four overlapping release sections collapsed to one, stale v1->v35 schema history dropped (it lives in CHANGELOG), marketplace endpoint internals and verbose process sections moved out or tightened. New focused docs: - docs/RELEASING.md - release process, deploy workflows, CI quirks (RELEASE_TEMPLATE.md folded in as an appendix) - docs/marketplace.md - marketplace ingestion + re-serving internals - docs/README.md - documentation index by audience, linked from README.md and CLAUDE.md Archived under docs/archive/: docs/superpowers/ (52 historical planning artifacts), HACKATHON.md, pd-ps-comments.md, security-audit-2026-04.md, future/NOTIFICATIONS.md. Removed the docs/auto-install.md stub. Fixed dangling links in connectors/jira/README.md and dev_docs/README.md, repointed code/doc references to archived paths.
6.2 KiB
Data Access Control — Spec
Date: 2026-03-31 Status: Draft
1. Problem
V novém systému (API místo rsync) nemáme ekvivalent rsync filtru. Každý přihlášený uživatel vidí a stáhne všechny tabulky. V produkci to řeší filesystem permissions + per-user rsync filter.
2. Současný model (produkce, rsync)
Server: /data/src_data/parquet/
├── crm/orders.parquet ← dataread group
├── crm/customers.parquet ← dataread group
├── private/salaries.parquet ← data-private group only
└── jira/issues/2026-03.parquet ← dataread group
Analytik (sync_data.sh):
1. Webapp generuje ~/.sync_rsync_filter (include/exclude per tabulka)
2. rsync --filter="merge ~/.sync_rsync_filter" stáhne jen povolené
3. AI agent pracuje s lokálními soubory → vidí jen to co se stáhlo
Tři vrstvy:
- Linux skupiny (dataread, data-private) → hrubé řízení
- Datasety (opt-in v instance.yaml) → celé skupiny tabulek
- Per-table subscription (explicit mode) → jednotlivé tabulky
3. Nový model (API)
Princip zůstává: uživatel vidí jen to, k čemu má explicitní přístup.
3.1 Datový model
Stávající tabulka dataset_permissions v DuckDB:
CREATE TABLE dataset_permissions (
user_id VARCHAR NOT NULL,
dataset VARCHAR NOT NULL, -- table_id nebo dataset group name
access VARCHAR DEFAULT 'read', -- 'read', 'none'
PRIMARY KEY (user_id, dataset)
);
dataset může být:
- Table ID (
circle,chart_of_accounts) — přístup k jedné tabulce - Wildcard/group (
in.c-finance.*) — přístup ke všem tabulkám v bucketu - Dataset name (
jira,finance) — pojmenovaná skupina z instance.yaml
3.2 Pravidla přístupu
Admin → vidí vše (bypass permissions)
Ostatní → vidí jen tabulky kde:
1. Existuje explicitní permission (dataset_permissions.access = 'read')
2. NEBO tabulka patří do povoleného datasetu/bucketu
3. NEBO je tabulka public (nový flag v table_registry)
3.3 Nový sloupec v table_registry
ALTER TABLE table_registry ADD COLUMN is_public BOOLEAN DEFAULT true;
is_public = true→ každý přihlášený uživatel vidí (default, zpětně kompatibilní)is_public = false→ vyžaduje explicitní permission
4. Kde se kontroluje
4.1 Manifest (GET /api/sync/manifest)
# Současný kód (NEFUNGUJE):
accessible = set(perm_repo.get_accessible_datasets(user["id"]))
# ... ale nikdy nefiltruje
# Nový kód:
all_states = repo.get_all_states()
if user["role"] != "admin":
all_states = [s for s in all_states if _user_can_access(user, s["table_id"])]
4.2 Download (GET /api/data/{table}/download)
# Současný kód (ŽÁDNÁ KONTROLA):
return FileResponse(path=file_path)
# Nový kód:
if not _user_can_access(user, table_id):
raise HTTPException(403, "Access denied")
return FileResponse(path=file_path)
4.3 Query (POST /api/query)
# Současný kód: otevře analytics.duckdb s VŠEMI views
# Nový kód: vytvořit per-user filtered connection
# Varianta A: CREATE TEMP VIEW pro povolené tabulky
# Varianta B: Dynamicky generovat allowed list, validovat SQL against it
Query je nejtěžší — uživatel může napsat SELECT * FROM salaries a pokud view existuje v analytics.duckdb, data se vrátí. Řešení:
Varianta A — Filtered views (doporučeno): Per-request vytvoření in-memory DuckDB, ATTACH analytics.duckdb, vytvořit views jen pro povolené tabulky. Overhead ~10ms.
Varianta B — SQL validation: Parsovat SQL, extrahovat referenced tables, ověřit proti allowed list. Křehké (sub-queries, CTEs, aliasy).
4.4 Catalog (GET /api/catalog/tables)
# Filtrovat jako manifest — uživatel vidí metadata jen povolených tabulek
if user["role"] != "admin":
tables = [t for t in tables if _user_can_access(user, t["id"])]
5. Shared helper
# src/rbac.py — rozšíření
def can_access_table(user: dict, table_id: str) -> bool:
"""Check if user can access a specific table."""
# Admin bypass
if user.get("role") == "admin":
return True
# Check if table is public
table = TableRegistryRepository(conn).get(table_id)
if table and table.get("is_public", True):
return True
# Check explicit permission
user_id = user["id"]
if DatasetPermissionRepository(conn).has_access(user_id, table_id):
return True
# Check wildcard/bucket permission (e.g., "in.c-finance.*")
bucket = table.get("bucket", "") if table else ""
if bucket and DatasetPermissionRepository(conn).has_access(user_id, f"{bucket}.*"):
return True
return False
6. Admin API pro permissions
POST /api/admin/permissions — grant access
DELETE /api/admin/permissions — revoke access
GET /api/admin/permissions/{user_id} — list user's permissions
GET /api/admin/permissions — list all (admin only)
POST body: {"user_id": "...", "dataset": "circle", "access": "read"}
7. Migrace
Pro existující instance:
- Všechny stávající tabulky:
is_public = true(zachová současné chování) - Admin nastaví
is_public = falsepro citlivé tabulky - Přidá explicitní permissions pro uživatele
Pro nové instance:
- Default
is_public = true→ otevřený model (jako teď) - Admin může přepnout na uzavřený:
is_public = falseper tabulka
8. CLI (da sync)
da sync
→ GET /api/sync/manifest (vrátí jen povolené tabulky)
→ pro každou tabulku: GET /api/data/{table}/download
→ rebuild lokální DuckDB jen z povolených parquetů
→ AI agent vidí jen to co se stáhlo
Identický princip jako rsync filter — ale filtr je server-side v API, ne v souboru.
9. Co se NEMĚNÍ
- Role hierarchy (viewer < analyst < km_admin < admin)
- Admin vidí vše
- JWT auth flow
- Orchestrator + extractory (server-side, vidí vše)
- Sync trigger (admin-only, stahuje vše na server)
10. Implementační pořadí
is_publicsloupec v table_registry (schema v3)can_access_table()helper v src/rbac.py- Filtrování v manifest + download + catalog
- Admin permissions API
- Query endpoint — filtered views
- Testy