tenants table: add UNIQUE constraints (jail_root_path, collection) + lifecycle tests #93

Closed
opened 2026-06-20 06:26:52 +02:00 by clawdie · 1 comment
Owner

Context

The tenants table is merged to main (register_tenant + export_json now includes tenants — that part is done). Two hardening items remain from review.

1. UNIQUE constraints (security, not hygiene)

  • UNIQUE(jail_root_path) — without it, two tenants could map to the same jail root → cross-tenant .env writes. Pairs with #91's root verification.
  • Collection uniquenesscoordinate with #88 first. If #88 lands as recommended (collection name = tenant_id), collection uniqueness is already implied by the tenant_id PRIMARY KEY and a separate collection_id column becomes redundant. Only add UNIQUE(collection_id) if that column survives the #88 decision.

Migration note: additive, but adding UNIQUE to an existing table fails if duplicate rows exist — dedupe first (currently only a test tenant, so low risk).

2. Tenant lifecycle tests

Add: register/get, list sorted by tenant_id, status update, invalid status rejected (CHECK), and export-includes-tenants.

Acceptance

Duplicate jail_root_path (and collection_id if retained) are rejected at the DB layer; tenant lifecycle is test-covered.

Related: #88 (collection identifier decision), #91 (jail-root verification).

🤖 Generated with Claude Code

## Context The `tenants` table is merged to main (`register_tenant` + `export_json` now includes tenants — that part is done). Two hardening items remain from review. ## 1. UNIQUE constraints (security, not hygiene) - **`UNIQUE(jail_root_path)`** — without it, two tenants could map to the same jail root → cross-tenant `.env` writes. Pairs with #91's root verification. - **Collection uniqueness** — **coordinate with #88 first.** If #88 lands as recommended (collection *name* = `tenant_id`), collection uniqueness is already implied by the `tenant_id` PRIMARY KEY and a separate `collection_id` column becomes redundant. Only add `UNIQUE(collection_id)` if that column survives the #88 decision. Migration note: additive, but adding UNIQUE to an existing table fails if duplicate rows exist — dedupe first (currently only a test tenant, so low risk). ## 2. Tenant lifecycle tests Add: register/get, list sorted by `tenant_id`, status update, invalid status rejected (CHECK), and export-includes-tenants. ## Acceptance Duplicate `jail_root_path` (and `collection_id` if retained) are rejected at the DB layer; tenant lifecycle is test-covered. Related: #88 (collection identifier decision), #91 (jail-root verification). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Closing — everything #93 asked for is already on main (verified in schema.rs + store tests); my earlier review was of branch 6b16281 before these landed and grepped lib.rs instead of schema.rs:

  • jail_root_path TEXT NOT NULL UNIQUE
  • collection_id TEXT NOT NULL UNIQUE
  • export_json includes tenants
  • tenant tests: test_tenant_lifecycle, test_tenant_uniqueness

No remaining work. The collection_id migration nuance (can't be dropped via CREATE TABLE IF NOT EXISTS) is captured on #88.

🤖 Generated with Claude Code

Closing — everything #93 asked for is **already on main** (verified in `schema.rs` + store tests); my earlier review was of branch `6b16281` *before* these landed and grepped `lib.rs` instead of `schema.rs`: - `jail_root_path TEXT NOT NULL UNIQUE` ✅ - `collection_id TEXT NOT NULL UNIQUE` ✅ - `export_json` includes tenants ✅ - tenant tests: `test_tenant_lifecycle`, `test_tenant_uniqueness` ✅ No remaining work. The `collection_id` migration nuance (can't be dropped via `CREATE TABLE IF NOT EXISTS`) is captured on #88. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: clawdie/colibri#93
No description provided.