Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
209 commits
Select commit Hold shift + click to select a range
6ce00e2
chore: add schema creation script and update Directus workflow rules
spashii Apr 13, 2026
6f34b48
chore: create app_user collection
spashii Apr 13, 2026
31ab178
chore: create org and org_membership collections
spashii Apr 13, 2026
9492e65
chore: create workspace and workspace_membership collections
spashii Apr 13, 2026
686a81a
chore: create workspace_invite and project_membership collections
spashii Apr 13, 2026
7c6a071
chore: create usage_event collection
spashii Apr 13, 2026
705e1a9
chore: add workspace_id, visibility, and deleted_at to project
spashii Apr 13, 2026
1128430
chore: add deleted_at to conversation, project_chat, project_report
spashii Apr 13, 2026
b424d97
chore: remove legacy chat collection
spashii Apr 13, 2026
46200ed
docs: add workspaces planning docs and codebase exploration report
spashii Apr 13, 2026
1d332d8
refactor: swap DirectusClient from requests to httpx
spashii Apr 13, 2026
0466d6f
feat: add AsyncDirectusClient for non-blocking FastAPI endpoints
spashii Apr 16, 2026
35281fe
chore: remove usage_event collection
spashii Apr 16, 2026
948890c
chore: add deleted_at to project_webhook
spashii Apr 16, 2026
bee6bfc
refactor: convert project delete to soft delete via BFF
spashii Apr 16, 2026
6d23381
refactor: convert conversation delete to soft delete
spashii Apr 16, 2026
43e2d16
refactor: convert project_chat delete to soft delete via BFF
spashii Apr 16, 2026
2b5e4e2
refactor: convert project_report delete to soft delete
spashii Apr 16, 2026
54afb0b
refactor: convert project_webhook delete to soft delete
spashii Apr 16, 2026
25cb4e8
refactor: reroute tag deletes through Python API
spashii Apr 16, 2026
bb02811
refactor: add deleted_at IS NULL filters to all read queries
spashii Apr 16, 2026
24b94f9
chore: remove DELETE permissions from Basic User policy
spashii Apr 16, 2026
35a0814
feat: add PostHog analytics integration to frontend
spashii Apr 16, 2026
f6d982e
chore: scaffold v2 API structure
spashii Apr 16, 2026
2ba4193
feat: implement GET /v2/me endpoint
spashii Apr 16, 2026
80efb64
feat: implement POST /v2/onboarding/complete
spashii Apr 16, 2026
b085fa6
feat: v2 API endpoints, invite flow, email, and pre-seed tooling
spashii Apr 16, 2026
66db587
feat: onboarding page, login redirect, and header menu integration
spashii Apr 16, 2026
208c0d2
feat: workspace usage stats, team rollups, and create workspace endpoint
spashii Apr 16, 2026
91fa1fe
feat: workspace selector, create workspace, topbar, and security fixes
spashii Apr 16, 2026
6d8442d
fix: set workspace context after onboarding completion
spashii Apr 16, 2026
b40d708
fix: auto-set workspace context for users who onboarded before contex…
spashii Apr 16, 2026
1637c85
feat: v2 workspace-scoped project list and create endpoints
spashii Apr 16, 2026
0d3ba76
feat: workspace-scoped projects (v2 API + frontend wiring)
spashii Apr 16, 2026
d7b4941
refactor: replace localStorage workspace with server-derived state
spashii Apr 16, 2026
756aecc
feat: workspace settings page, blocker fixes, and security hardening
spashii Apr 16, 2026
5f2f11d
fix: comprehensive UX, brand, and security polish
spashii Apr 16, 2026
dd93052
feat: monthly usage breakdown on workspace cards and team rollups
spashii Apr 16, 2026
1c49fb8
fix: security + email fixes from final audit
spashii Apr 16, 2026
7c3db76
perf: parallelize workspace list queries + fix pre-seed script
spashii Apr 16, 2026
b54a044
fix: brand consistency across ALL email templates
spashii Apr 16, 2026
5aa191b
fix: workspace switching, header alignment, invite validation, page c…
spashii Apr 16, 2026
efa1d42
feat: add Manage button on workspace cards for admin/owner
spashii Apr 16, 2026
7a78b35
feat: show user's role and effective policies on workspace settings
spashii Apr 16, 2026
fcc6bd7
feat: /workspaces/:id redirects to /workspaces/:id/settings
spashii Apr 16, 2026
1925651
feat: expanded policy system with tier enforcement + missing checks
spashii Apr 16, 2026
bb5c8f3
feat: URL-based workspace routing (/w/:workspaceId/projects)
spashii Apr 16, 2026
0b12fa2
fix: expand owner policies, show pending invites, fix member card bor…
spashii Apr 16, 2026
f78d193
fix: critical security + UX fixes from final review
spashii Apr 16, 2026
a97606b
feat: role management, invite cancellation, and participant portal fix
spashii Apr 16, 2026
9216f72
feat: workspace rename UI + header home respects workspace context
spashii Apr 16, 2026
eae799d
feat: PATCH /v2/me for profile edit + scope conversation move to work…
spashii Apr 16, 2026
e083248
feat: script to create user_project_pin junction table
spashii Apr 16, 2026
9021900
fix: surface email send failures instead of silently dropping
spashii Apr 16, 2026
b4a77d9
feat: invite modal, resend invite, native confirm → Mantine modals
spashii Apr 16, 2026
1f98f82
fix: DELETE /v2/workspaces/:id/invites/:iid hard-deletes the row
spashii Apr 16, 2026
d1b3f0d
fix: allow re-inviting emails whose previous invite was cancelled/exp…
spashii Apr 16, 2026
37a35b9
feat: show inviter name + list/accept/decline my invites
spashii Apr 16, 2026
375344a
feat: show inviter name on pending invite rows
spashii Apr 16, 2026
4b855d7
fix: email logo URL + final brand compliance pass
spashii Apr 16, 2026
243f478
feat: human-centered invite flow with pending invites page + honeypot
spashii Apr 16, 2026
8aa9b44
feat: HMAC hash in invite URL + stepper registration + last name opti…
spashii Apr 17, 2026
cac5561
feat: remember last-used workspace for returning users
spashii Apr 17, 2026
48d0948
fix: satisfy workspace_invite.token NOT NULL constraint
spashii Apr 17, 2026
0a37fb1
chore: remove unused workspace_invite.token field from schema
spashii Apr 17, 2026
38ffb66
fix: code smells from audit — brand, unused code, silent bugs
spashii Apr 17, 2026
7fb9841
fix: accept-by-hash handles already-accepted invites + brand refresh
spashii Apr 17, 2026
050cb81
fix: add URL allow lists to Directus env sample (unblocks password re…
spashii Apr 17, 2026
efb260c
feat: install SSH server in devcontainer (port 2222 → 22)
spashii Apr 17, 2026
94cf40d
feat: derived inheritance module + tier auto-wire in has_policy
spashii Apr 20, 2026
818c774
feat: surface is_staff in /v2/me
spashii Apr 20, 2026
9736a2c
feat: /v2/orgs endpoints for team management
spashii Apr 20, 2026
43f8649
docs: workspaces release sources of truth
spashii Apr 20, 2026
4141262
feat: tier mgmt, upgrade-request, delete workspace, downgrade effects
spashii Apr 20, 2026
e26d725
refactor: shared email layout + auto plain-text fallbacks
spashii Apr 20, 2026
c51a7e0
feat: private project sharing API — /v2/projects/:id/members CRUD
spashii Apr 20, 2026
e66c483
feat: onboarding split — differentiate new vs legacy users
spashii Apr 20, 2026
00613dd
docs: status report for autonomous S5–S14 run
spashii Apr 20, 2026
15c7d1a
fix(inheritance): onboarding writes creator as source='direct'
spashii Apr 20, 2026
2f543ac
fix(security): upgrade-request uses Jinja template + rate limit + sub…
spashii Apr 20, 2026
ff93e68
fix(security): whitelabel tier gate + URL validation + NULL settings …
spashii Apr 20, 2026
0120a72
fix(inheritance): team-owner carve-out + sticky_remove on member removal
spashii Apr 20, 2026
f2bfb2f
docs: audit summary + fix/defer ledger for the 5-perspective review
spashii Apr 20, 2026
0cbb3b9
chore(deploy): legacy inherited-row migration + env sample for email
spashii Apr 21, 2026
001ef0a
feat: S10 private project sharing UI + visibility toggle endpoint
spashii Apr 21, 2026
d042a85
feat: S11 tier-gate FeatureGate + UpgradeModal components
spashii Apr 21, 2026
f2cf0a0
feat: S12 selector polish — team hero + hover-manage + guest-of pills
spashii Apr 21, 2026
8aba15d
fix: round-2 audit — 7 of 8 critical/high findings addressed
spashii Apr 21, 2026
4646825
feat: private projects — read-time enforcement on common surfaces
spashii Apr 21, 2026
deb6597
fix(workspace): security + footgun pass from audits
spashii Apr 21, 2026
a85d2fe
feat(workspace): team page, sharing tab, access bubbles, /w URL, hard…
spashii Apr 21, 2026
997ab26
feat(workspace): /t/ team route, settings pages, emails in lists, aud…
spashii Apr 21, 2026
505ba73
feat(ux): dotted +workspace card, /w sweep, audit fixes
spashii Apr 21, 2026
b71a2d7
feat(inbox): notifications table + service + emit backfill
spashii Apr 21, 2026
1b2ed00
feat(inbox): notifications drawer + more emit coverage
spashii Apr 21, 2026
00b3218
feat(inbox): more emit sites + dead-code sweep
spashii Apr 21, 2026
cfa758e
feat(inbox): close INVITE_ACCEPTED + TEAM_MEMBER_ADDED on remaining p…
spashii Apr 21, 2026
bc3310c
chore(workspaces): in-flight polish — notifications unification, emai…
spashii Apr 23, 2026
ec67257
chore(workspaces): default upgrade-inbox to upgrades@dembrane.com
spashii Apr 23, 2026
b9c0b48
S0: backfill script for direct-membership walkback (dry-run default)
spashii Apr 23, 2026
28ff732
S6: roles — retire viewer, add billing preset, wire upgrade:request p…
spashii Apr 23, 2026
a815f15
S10a: add workspace.visibility enum (open_to_team | private)
spashii Apr 23, 2026
e734319
S13: GET /v2/workspaces/:id/usage + tier_capacity matrix
spashii Apr 23, 2026
21e666f
S14: Pilot hard-block on host-side endpoints (matrix §8)
spashii Apr 23, 2026
b49c30d
S3a: post-downgrade tracking + email to admin+billing (matrix §3)
spashii Apr 23, 2026
c8fae01
S6: Slack-style discovery + join + access-request endpoints (matrix §6)
spashii Apr 23, 2026
385bf8f
S14b: Pilot hard-block on v1 chat + agentic endpoints (matrix §8)
spashii Apr 23, 2026
1feb963
matrix §6: set workspace.visibility on create + honesty disclosure on…
spashii Apr 23, 2026
c403b8c
matrix §6: guard + legacy-write cleanup
spashii Apr 23, 2026
c59b681
matrix §3: move downgrade email to Dramatiq network queue
spashii Apr 23, 2026
f90ab7b
frontend: Pilot-block modal + 7-day downgrade banner (matrix §3 + §8)
spashii Apr 23, 2026
f1b7e30
frontend §6: discovery section on home + access-requests list on sett…
spashii Apr 23, 2026
58d462f
frontend §8: workspace usage card on settings page
spashii Apr 23, 2026
5de8891
server-cache the usage endpoint (30m TTL + ?refresh) + drop broken ta…
spashii Apr 23, 2026
35b1705
matrix §3: auto-return DowngradeBanner on frozen-feature-attempt
spashii Apr 23, 2026
893be3f
HCD audit — tier-1 fixes (pattern + copy hygiene)
spashii Apr 23, 2026
7139a30
HCD audit — tier-2 fixes (action affordances)
spashii Apr 23, 2026
e4387e9
matrix §8 team-scope: GET /v2/orgs/:id/usage + TeamUsageRollup strip
spashii Apr 23, 2026
c2c3fb1
matrix §1: full tier capacity matrix in-product (UpgradeModal)
spashii Apr 23, 2026
963fb0b
HCD tier-3: admin chips + Private tier-gate + no dead-clicks on /t/
spashii Apr 23, 2026
79fc727
workspace settings — Overview / Billing tab split
spashii Apr 23, 2026
bdd16dd
fix: seat count double-counted when a user has duplicate memberships
spashii Apr 23, 2026
5ec8cd4
fix: team matrix hid direct workspace memberships for non-admin members
spashii Apr 23, 2026
1ff2d5a
team usage: per-workspace breakdown with at/approaching-cap rows
spashii Apr 23, 2026
868ffba
S7: team admin Projects view on /t/:orgId (matrix §4 wind-down workflow)
spashii Apr 23, 2026
da70a10
S9: workspace creation wizard (3-step)
spashii Apr 23, 2026
81cbb5b
invite-as-guest path in the workspace invite modal
spashii Apr 23, 2026
31d2963
home cards: role chip + tier badge side-by-side (matrix §2 UX)
spashii Apr 23, 2026
a9f45b7
matrix §4: delete-workspace UI in Danger tab (admin + owner)
spashii Apr 23, 2026
961c72b
home team hero: inline health hint (hours, overage, cap warnings)
spashii Apr 23, 2026
3602536
workspace cards show at/approaching cap warnings on home
spashii Apr 23, 2026
fcb09d2
matrix §10: partner-client model — schema + handoff + referral ledger
spashii Apr 23, 2026
e03f74f
dev: scripts/seed_dev.py — reset + seed with toy examples for the rel…
spashii Apr 23, 2026
3f42f83
demo-feedback pass 1: nav + team header + wizard copy
spashii Apr 23, 2026
7bc8cf5
team page rework: Usage / People / Workspaces / Projects view switcher
spashii Apr 23, 2026
0d64909
workspace home layout: summary tile up top, create-in-row, pinned below
spashii Apr 23, 2026
7b93ab5
search state → URL (?search=) across selector + team + team-projects
spashii Apr 23, 2026
8a8481c
fix: settings page crash — useSearchParams called after early return
spashii Apr 23, 2026
f411a4c
demo feedback pass 2: drop "approaching" + forecast + inheritance UI;…
spashii Apr 23, 2026
7dcd483
team page: Mantine tabs (Overview / Usage / People); fold settings in…
spashii Apr 23, 2026
2aa0de5
team projects: sort by conversation_count desc
spashii Apr 23, 2026
f940f31
team page: inline role change + role label case normalization
spashii Apr 23, 2026
ecadbf8
feat(workspace): logo URL → file upload
spashii Apr 23, 2026
15a9f24
feat(usage): period selector on workspace + team usage
spashii Apr 23, 2026
ece99b3
fix(ux): visual pass — autosave, plurals, tier tooltips, team logo up…
spashii Apr 23, 2026
032ebe7
fix(ux): showstoppers from 2026-04-23 audit
spashii Apr 23, 2026
da8edeb
fix(ux): workspace picker on nav + seat count zeroed out
spashii Apr 23, 2026
5ea7c48
fix(projects): pin now reflects without a page reload + refresh usage…
spashii Apr 23, 2026
b80e314
fix(ux): matrix honesty on access copy + retire Owner from the UI (A1…
spashii Apr 23, 2026
11859d2
fix(ux): tier tagline moves to Billing only; drop duplicate team stat…
spashii Apr 23, 2026
cb4440b
fix(ux): kill duplicate /t/:id/settings; team edit lives in Overview …
spashii Apr 23, 2026
77b4348
fix(ux): confirm before matrix role changes; row-menu on team project…
spashii Apr 23, 2026
4604b79
feat(ux): workspace Members tab + Team Projects tab (B3+B4+C5)
spashii Apr 23, 2026
7168d6a
polish: billing "/ ∞" on uncapped projects, pluralize, placeholder (C…
spashii Apr 23, 2026
83d115f
feat(ux): settings tab splits + project Sharing → section + subtle us…
spashii Apr 23, 2026
a8f27d4
feat(ux): workspace home — quiet identity, pinned on top, hero empty …
spashii Apr 23, 2026
18e28d9
polish: Team Overview danger footnote (§3)
spashii Apr 23, 2026
5dd4cef
fix(security+ux): SVG upload, brand/copy, empty states, header back link
spashii Apr 23, 2026
ac2f468
refactor(routing): unify tab URLs on path segments (team + workspace …
spashii Apr 23, 2026
4b5b5a8
refactor(ux): team + workspace-settings tabs live in the URL path
spashii Apr 23, 2026
b190459
fix(ux): workspace settings rules-of-hooks crash + drop legacy ?tab= …
spashii Apr 23, 2026
838ac3f
ux: logo click → /w, team + workspace logos breathe, card colors calm
spashii Apr 23, 2026
9f1a814
ux: Team tabs + usage table redesign + no silent "Replace logo"
spashii Apr 23, 2026
99ae7d1
feat(settings): "My access" section — every team + workspace a user c…
spashii Apr 23, 2026
ad5cb9d
ux: seats explainer on Billing + "Invite someone" on team People tab
spashii Apr 23, 2026
0607dfb
feat(team): Usage attention panel + seats column, People card list (o…
spashii Apr 23, 2026
78d2e8e
revert: drop Referrals tab until the kickback model is settled
spashii Apr 23, 2026
79174b7
schema: step 14 — kickback extensions on referral_ledger
spashii Apr 23, 2026
88b3d0d
fix(onboarding+selector): 500 on /v2/onboarding/complete + empty stat…
spashii Apr 23, 2026
0ed35c5
fix(project): drop duplicate heading, surface workspace usage, fix pi…
spashii Apr 23, 2026
183e709
ux: kill usage strip on project page, rename Billing/Usage tabs to "U…
spashii Apr 23, 2026
aa971a4
chore: ignore scripts/__pycache__
spashii Apr 23, 2026
b6f6656
feat(chat): workspace-scoped templates (schema + backend + UI toggle)
spashii Apr 23, 2026
52802ee
fix: three pains from the validation log (onboarding retry, billing a…
spashii Apr 23, 2026
8872267
ux(projects): gate pin + create for guest/external workspace access
spashii Apr 23, 2026
aaf5641
ux(auth): add recovery affordances on check-your-email screen
spashii Apr 23, 2026
a6c05cd
docs: overnight pass summary in 05-PROGRESS.md
spashii Apr 23, 2026
a22829b
fix(access_requests): integer PK + surface schema drift in search()
spashii Apr 24, 2026
0b421b7
schema: run step 9 + drop the always-failing ref_report_id FK
spashii Apr 24, 2026
b20f835
schema: access_request.id now uuid from the start (+ step 16 repair)
spashii Apr 24, 2026
b7d9caa
chore: matrix v1.1 conformance smoke test
spashii Apr 24, 2026
76eeb78
fix(inbox): "For you" notifications can always be marked read
spashii Apr 24, 2026
40b7e6a
feat(admin): staff-only /admin surface — billing rollup, at-risk, par…
spashii Apr 24, 2026
308428a
feat(admin): Usage and Billing rewrite on TanStack Table, partners + …
spashii Apr 24, 2026
8a0461c
refactor(admin): columns reordered, kpi cards removed, real pricing m…
spashii Apr 24, 2026
972c300
ui(team): Usage and Tier tab picks up the admin-table pattern (no pri…
spashii Apr 24, 2026
55df4e9
fix(tables): column-toggle robustness, distinct totals row, replace "…
spashii Apr 24, 2026
d189caa
ui(tables): sortable columns advertise themselves + full-cell click t…
spashii Apr 24, 2026
ffbd264
feat(tables): guest seat count column on admin and team usage tables
spashii Apr 24, 2026
90ac338
saved the wip
spashii Apr 28, 2026
608838c
merge with main
spashii Apr 28, 2026
9ef760f
refactor(workspaces): rename "team" → "organisation" + lint sweep
ussaama Apr 29, 2026
283cdcc
- fix(workspaces): hide private workspaces from members; sync visibil…
ussaama May 4, 2026
04390ef
feat(workspaces): seat/guest cap enforcement + guest scope hardening
ussaama May 5, 2026
98add8d
fix(billing): persist audio hours through soft-delete, improve empty …
ussaama May 6, 2026
be3ca3f
fix: harden invite accept, verify-email, and registration flows
ussaama May 6, 2026
393d877
fix(auth): harden login/logout flow for multi-workspace sessions
ussaama May 7, 2026
eba991e
feat: access-denied UI, billing filter chip, sole-admin lock, org cou…
ussaama May 7, 2026
8e59259
fix: surface silent errors across workspace and organisation features
ussaama May 7, 2026
1f3cff7
Move whitelabel logo to workspace level with tier gating
ussaama May 7, 2026
d1b4939
Clear whitelabel logo on logout by gating the logo effect on auth state
ussaama May 7, 2026
8b6ceed
fix: treat global routes as outside-workspace for logo resolution
ussaama May 8, 2026
0ba5830
fix(usage): repair Columns filter and align table layout on org usage…
ussaama May 8, 2026
a0b9613
style(org): align organisation pages to brand guidelines
ussaama May 8, 2026
f0f863b
fix(usage): bust rollup cache + query keys after billable mutations
ussaama May 8, 2026
b6d8715
chore(lint): fix ruff and biome errors blocking CI
ussaama May 8, 2026
0125b64
fix(security): resolve CodeQL findings blocking workspaces → main
ussaama May 8, 2026
13a3680
fix(security): resolve remaining CodeQL findings
ussaama May 8, 2026
06dd277
fix(security): address remaining CodeQL findings
ussaama May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions echo/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/universal
{
"name": "dembrane/echo",
"name": "dembrane",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
// "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"dockerComposeFile": "docker-compose.yml",
Expand Down Expand Up @@ -49,8 +49,8 @@
"5432": {
"label": "postgres"
},
"9001": {
"label": "minio-ui"
"2222": {
"label": "ssh"
}
},
// Configure tool-specific properties.
Expand Down
21 changes: 1 addition & 20 deletions echo/.devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,6 @@ services:
networks:
- dembrane-network

neo4j:
image: neo4j:5.26.16-community
restart: unless-stopped
volumes:
- ./neo4j_data/logs:/logs
- ./neo4j_data/config:/config
- ./neo4j_data/data:/data
- ./neo4j_data/plugins:/plugins
ports:
- 7474:7474 # Neo4j Browser
- 7687:7687 # Neo4j Bolt protocol
environment:
- NEO4J_AUTH=neo4j/admin@dembrane
networks:
- dembrane-network

directus:
build:
context: ../directus
Expand Down Expand Up @@ -104,6 +88,7 @@ services:
- 8000:8000
- 5173:5173
- 5174:5174
- 2222:22
environment:
- DIRECTUS_SECRET=secret
- DIRECTUS_TOKEN=admin
Expand All @@ -120,9 +105,6 @@ services:
- STORAGE_S3_SECRET=dembrane
- STORAGE_S3_BUCKET=dembrane
- STORAGE_S3_ENDPOINT=http://minio:9000
- NEO4J_URI=bolt://neo4j:7687
- NEO4J_USERNAME=neo4j
- NEO4J_PASSWORD=admin@dembrane

volumes:
- ../..:/workspaces:cached
Expand All @@ -134,7 +116,6 @@ services:
depends_on:
- postgres
- redis
- neo4j

networks:
dembrane-network:
Expand Down
72 changes: 72 additions & 0 deletions echo/.devcontainer/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,66 @@ install_server_deps() {
fi
}

install_ssh_server() {
if command_exists sshd && [ -f /etc/ssh/sshd_config ]; then
log_info "openssh-server already installed"
else
ensure_apt_packages openssh-server
log_info "openssh-server installed"
fi

# Generate host keys if missing
if [ ! -f /etc/ssh/ssh_host_ed25519_key ]; then
log_info "Generating SSH host keys..."
ssh-keygen -A
fi

# Ensure /var/run/sshd exists (required for sshd to start)
mkdir -p /var/run/sshd

# Configure sshd: allow root login (dev container runs as root),
# enable password auth for first-time setup, permit public key auth.
local sshd_config="/etc/ssh/sshd_config.d/99-devcontainer.conf"
cat > "$sshd_config" <<'CONF'
# Devcontainer SSH config
Port 22
PermitRootLogin yes
PasswordAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PermitEmptyPasswords no
X11Forwarding yes
AllowAgentForwarding yes
AllowTcpForwarding yes
PrintMotd no
UsePAM yes
AcceptEnv LANG LC_*
Subsystem sftp /usr/lib/openssh/sftp-server
CONF

# Set a default root password so initial SSH works
# (override in your own setup with ssh-copy-id to switch to key auth)
if ! passwd -S root 2>/dev/null | grep -q "^root P "; then
log_info "Setting default root password (change after first login)"
echo 'root:dembrane' | chpasswd
fi

# Ensure .ssh dir exists for authorized_keys
mkdir -p /root/.ssh
chmod 700 /root/.ssh
touch /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys

# Start sshd (not systemd-managed in container — run directly)
if pgrep -x sshd > /dev/null; then
log_info "sshd already running"
else
log_info "Starting sshd..."
/usr/sbin/sshd
fi
log_info "SSH server ready on port 22 (user: root, password: dembrane)"
}

install_postgresql_client() {
if command_exists psql && psql --version | grep -q "psql (PostgreSQL) 16"; then
log_info "PostgreSQL client 16 already installed: $(psql --version)"
Expand Down Expand Up @@ -289,6 +349,7 @@ Options:
--skip-server Skip server dependency installation
--skip-python Skip managed Python setup for uv
--skip-postgres Skip PostgreSQL client installation
--skip-ssh Skip SSH server installation

Environment overrides:
NODE_VERSION (default: ${NODE_VERSION})
Expand All @@ -305,6 +366,7 @@ parse_args() {
SKIP_SERVER="false"
SKIP_PYTHON="false"
SKIP_POSTGRES="false"
SKIP_SSH="false"

while [[ $# -gt 0 ]]; do
case "$1" in
Expand Down Expand Up @@ -340,6 +402,10 @@ parse_args() {
SKIP_POSTGRES="true"
shift
;;
--skip-ssh)
SKIP_SSH="true"
shift
;;
*)
log_error "Unknown option: $1"
show_help
Expand Down Expand Up @@ -412,6 +478,12 @@ main() {
log_info "Skipping PostgreSQL client installation"
fi

if [ "$SKIP_SSH" = "false" ]; then
install_ssh_server
else
log_info "Skipping SSH server installation"
fi

if [ "$SKIP_FRONTEND" = "false" ]; then
install_frontend_deps
else
Expand Down
2 changes: 1 addition & 1 deletion echo/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ echo-gitops/

cypress/reports/

cookies.txt
cookies.txtscripts/__pycache__/
2 changes: 1 addition & 1 deletion echo/.vscode/sessions.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://cdn.statically.io/gh/nguyenngoclongdev/cdn/main/schema/v11/terminal-keeper.json",
"active": "default",
"keepExistingTerminals": false,
"keepExistingTerminals": true,
"sessions": {
"default": [
{
Expand Down
117 changes: 117 additions & 0 deletions echo/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# CLAUDE.md

Rules and conventions for working on the ECHO codebase. Follow these precisely.

## Directus Rules (Critical)

**Never hand-write Directus sync/snapshot JSON files.** To create or modify Directus collections:

1. Write a Python script (e.g., `scripts/create_schema.py`) that uses the Directus REST API (`POST /collections`, `POST /fields`, `POST /relations`) with the admin token
2. Make scripts idempotent (check `collection_exists()` / `field_exists()` before creating)
3. Run the script step-by-step to verify each change
4. After all changes, pull the schema: `cd directus && bash sync.sh -u http://directus:8055 -e admin@dembrane.com -p admin pull`
5. Commit the sync output (the JSON files in `directus/sync/snapshot/`)

See `scripts/create_schema.py` for the established pattern (Session 2 workspaces schema).

### Python DirectusClient

- `create_item` / `update_item` return `{"data": {...}}` — **MUST** unwrap with `["data"]`
- `get_items` / `get_item` return data directly (no wrapper)
- `get_items` requires `{"query": {filter, fields, sort, ...}}` wrapper
- `search()` silently returns `{"error": "..."}` on failure — always validate return is a list before iterating

```python
# CORRECT
new = client.create_item("collection", {...})["data"]
items = client.get_items("collection", {"query": {"filter": {...}}})
if not isinstance(items, list):
items = []

# WRONG — missing ["data"] unwrap
new = client.create_item("collection", {...})
# WRONG — missing "query" wrapper
items = client.get_items("collection", {"filter": {...}})
```

### TypeScript Directus SDK

- Auto-unwraps everything — no `["data"]` needed
- If there's a type error with `<relationship>.count`, add it to `typesDirectus.d.ts` and use `count("<relationship>")` in fields

See `memory/directus-rules.md` for comprehensive patterns.

## Brand & UI Copy

Follow `brand/STYLE_GUIDE.md` for all user-facing text:

- **Never say "AI"** — use "language model" or just describe the action ("Generating your report..." not "Generating report with AI...")
- **Never say "successfully"** — just state what happened ("Saved" not "Successfully saved")
- **"dembrane" always lowercase**, even at sentence start
- **Never use bold for emphasis** — use Royal Blue (#4169e1) or italics
- Say "participants/hosts" not "users"
- Dutch translations: use informal "je/jij" form, keep English terms when they sound better (Dashboard, Upload, Chat)

## UI Rules

- **Never stack multiple Alert components** — show either the error alert or the info alert, not both
- **Don't use `@mantine/charts`** — use better charting libraries
- **Loading spinners**: always use `alwaysDembrane` prop on `DembraneLoadingSpinner` for whitelabel safety; never `animate-spin` on custom logos
- **Show emails only on hover** — don't display them by default in lists
- **Conversations come from QR codes or uploads** — never add "new conversation" buttons in the UI
- **Prefer text buttons over icon-only buttons** for important actions (e.g., "Go full screen" should be a text button)

## Architecture Preferences

- **BFF pattern**: move frontend Directus SDK calls to backend `/bff/` routes. Frontend should call aggregated API endpoints, not make multiple Directus queries
- **URL-driven state**: use URL search params (not React state) so state is shareable and persistent
- **SSE for progress**: use Server-Sent Events + Redis pub/sub for real-time progress (report generation, health streams)
- **No asyncio in Dramatiq actors**: use gevent pools + dramatiq groups instead. Report generation is fully synchronous
- **gevent.pool.Pool only in `network` queue** (uses `dramatiq-gevent`). CPU queue runs standard dramatiq
- **Use `gevent.sleep()` not `time.sleep()`** in network-queue actors

## LLM Model Groups

- `MULTI_MODAL_PRO` (Gemini 2.5 Pro) — chat, report generation, transcript correction. **Do not downgrade chat to Flash.**
- `MULTI_MODAL_FAST` (Gemini 2.5 Flash) — suggestions, verification, stateless endpoints, lightweight tasks
- `TEXT_FAST` (Azure GPT-4.1) — being deprecated, migrating to Gemini
- Report prompt templates are written IN the target language (not just instructing the LLM to write in that language)

## Translations

```bash
cd frontend
pnpm messages:extract # Extract new strings to .po files
# Edit .po files in src/locales/ (en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT)
pnpm messages:compile # Compile for production
```

Use `<Trans>` component or `` t` `` template literal from Lingui.

## Branching Strategy & Deployment

See [docs/branching_and_releases.md](docs/branching_and_releases.md) for the full guide.

Quick reference:
- **Feature flow**: branch off `main` → (optional) merge to `testing` for testing → PR to `main` → auto-deploys to Echo Next
- **Releases**: tagged from `main` every ~2 weeks → auto-deploys to production
- **Hotfixes**: branch off release tag → fix → new release → cherry-pick into main
- Always check for Directus data migrations before deploying (see `docs/database_migrations.md`)

## Transcription

- AssemblyAI `universal-3-pro` supports: en, es, pt, fr, de, it
- Dutch ("nl") requires `universal-2` fallback — `universal-3-pro` does NOT support it
- Production uses webhook mode (`ASSEMBLYAI_WEBHOOK_URL`), polling is only a fallback

## Dramatiq Tasks

- Restart workers after changing task signatures (positional args are serialized)
- `SkipRetryOnUnrecoverableError` middleware skips retries for TypeError, SyntaxError, AttributeError, ImportError, NotImplementedError
- When invoking async code from Dramatiq actors, use `run_async_in_new_loop` from `dembrane.async_helpers`

## Project Management

- Linear for issue tracking — tickets are `ECHO-xxx`
- Two-week cycles/sprints
- GitOps repo: `dembrane/echo-gitops` (separate repo)
7 changes: 6 additions & 1 deletion echo/directus/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ EMAIL_SMTP_PORT=587
EMAIL_SMTP_USER=""
EMAIL_SMTP_PASSWORD=""
EMAIL_SMTP_SECURE=false
EMAIL_SMTP_IGNORE_TLS=false
EMAIL_SMTP_IGNORE_TLS=false
# URL allow lists (Directus 11 requires these for auth redirect flows).
# Comma-separated. Add production + staging + local dev URLs.
PASSWORD_RESET_URL_ALLOW_LIST="https://dashboard.dembrane.com/password-reset"
USER_REGISTER_URL_ALLOW_LIST="https://dashboard.dembrane.com/verify-email"
USER_INVITE_URL_ALLOW_LIST="https://dashboard.dembrane.com/accept-invite"
Loading
Loading