docs: fix audit-found drift (status enum, env, limitations, action layout)

Audit follow-up. ARCHITECTURE: status now 'In production', authoritative
note that server actions are per-domain under lib/actions/* (no monolithic
lib/actions.ts), rewrote stale Known Limitations (test suite exists;
PayPal sync route; social auth wired; webhooks signing-only; single-space
assumption), fixed env table (removed phantom SUPABASE_JWT_SECRET, added
SECRETS_ENCRYPTION_KEY/OAuth). DATABASE_SCHEMA/DB_SCHEMA_MAP: ~43 tables /
migrations through 038; space_members.status is member_status enum default
'unverified' (was wrongly 'active'); fixed the active/pending flow text.
README: webhook wording matches WEBHOOKS.md. API_REFERENCE: real
lib/actions/* path + 4 previously-undocumented actions. createSecret docs
were already correct (ADMIN_ROLES = admin+board) -- left as-is.
This commit is contained in:
Moheeb Zara
2026-05-16 22:25:11 -07:00
parent dd9e3aced6
commit 3efa3cdb46
5 changed files with 37 additions and 21 deletions
+2 -2
View File
@@ -62,7 +62,7 @@
| `email` | text | nullable | | `email` | text | nullable |
| `handle` | text | nullable | | `handle` | text | nullable |
| `phone` | text | nullable | | `phone` | text | nullable |
| `status` | text | default `'active'` | | `status` | member_status enum (`current`,`late`,`inactive`,`unverified`) | default `'unverified'`; "active" = `current`/`unverified`/`late` |
| `payment_status` | text | nullable | | `payment_status` | text | nullable |
| `payment_note` | text | nullable | | `payment_note` | text | nullable |
| `joined_at` | timestamptz | | | `joined_at` | timestamptz | |
@@ -301,7 +301,7 @@ Default channels created by trigger on space INSERT: `general`, `announcements`,
### Login ### Login
1. `/login` → `signIn(email, password)` → Supabase auth 1. `/login` → `signIn(email, password)` → Supabase auth
2. Middleware checks session cookie 2. Middleware checks session cookie
3. App layout RSC calls `getCurrentMembership()` → queries `space_members` WHERE `user_id = auth.uid()` AND `status IN ('active', 'pending')` 3. App layout RSC resolves the member → queries `space_members` WHERE `user_id = auth.uid()` AND `status IN ('current','unverified','late')` (the `ACTIVE_STATUSES` set in `lib/permissions.ts`)
4. If no membership found → redirect `/signup` 4. If no membership found → redirect `/signup`
### Dashboard ### Dashboard
+1 -1
View File
@@ -24,7 +24,7 @@ A multi-tenant operating system for hackerspaces, makerspaces, and member-run sh
- **Governance.** Proposals with quorum, vote tracking and expiry; incident reports with anonymous reporter tracking by token; versioned policy library; member forum with polymorphic comments. - **Governance.** Proposals with quorum, vote tracking and expiry; incident reports with anonymous reporter tracking by token; versioned policy library; member forum with polymorphic comments.
- **Communications.** Multi-channel chat with realtime delivery. - **Communications.** Multi-channel chat with realtime delivery.
- **Recruitment.** Public-facing recruitment page per space. - **Recruitment.** Public-facing recruitment page per space.
- **API.** PostgREST-generated REST for every table, gated by RLS. Optional webhooks with HMAC-signed deliveries. - **API.** PostgREST-generated REST for every table, gated by RLS. Webhook HMAC signing contract + secret rotation are defined; per-event delivery is a follow-up (see [docs/WEBHOOKS.md](./docs/WEBHOOKS.md)).
## Stack ## Stack
+7 -1
View File
@@ -196,7 +196,7 @@ async function joinSpace(formData: {
## 3. Task Actions ## 3. Task Actions
**File**: `lib/actions.ts` **File**: `lib/actions/tasks.ts` (server actions are split per-domain under `lib/actions/*.ts`, re-exported from `lib/actions/index.ts`; there is no monolithic `lib/actions.ts`)
### `createTask(formData)` ### `createTask(formData)`
@@ -945,3 +945,9 @@ No permission code; any active member. All writes are self-only — the member i
- `getMyVisits()` — the caller's own recent visits (the `/me` history). - `getMyVisits()` — the caller's own recent visits (the `/me` history).
- `listAttendance()` — any member (org-wide attendance history is all-members by product decision); last 250 visits with computed status. No new permission code; managers are not special here. - `listAttendance()` — any member (org-wide attendance history is all-members by product decision); last 250 visits with computed status. No new permission code; managers are not special here.
Surfaced as the "Who's here" panel on the dashboard (present list + self check-in/out + host + note), the `/attendance` page (all members), and "My recent visits" on `/me`. Surfaced as the "Who's here" panel on the dashboard (present list + self check-in/out + host + note), the `/attendance` page (all members), and "My recent visits" on `/me`.
### Previously-undocumented actions (audit addendum)
- `updateMyProfile(input)` — `members.ts`; any member edits their own profile fields (self-scoped).
- `discloseAffiliations(input)` — `members.ts`; the member records their own conflict-of-interest affiliations.
- `updateSpaceVisibility(input)` — `settings.ts`; admin toggles space/member-directory visibility.
- `listDoorCards(input)` — `door.ts`; `door.operate`, service client; active cards for a connection's grant/revoke UI, returning only the masked `last4` (raw UID never returned).
+20 -11
View File
@@ -1,8 +1,15 @@
# Hackerspace.sh - Architecture Documentation # Hackerspace.sh - Architecture Documentation
> **Last Updated**: 2026-03-10 > **Last Updated**: 2026-05-17
> **Version**: 1.0.0 > **Version**: 1.0.0
> **Status**: Pre-Production Audit > **Status**: In production (serving live traffic)
>
> Note: Sections 7 and 13 and the project tree describe an earlier layout.
> Server actions are split per-domain under `lib/actions/*.ts` (re-exported
> from `lib/actions/index.ts`); there is no monolithic `lib/actions.ts`. The
> per-module behavior is current; the module-feature sections later in this
> document (Certifications, Classes, Equipment, Door, Presence, etc.) are the
> authoritative reference.
--- ---
@@ -395,7 +402,10 @@ Integration credentials stored in `integrations.config` (JSONB):
| `NEXT_PUBLIC_SUPABASE_URL` | Yes | Supabase project URL | | `NEXT_PUBLIC_SUPABASE_URL` | Yes | Supabase project URL |
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Yes | Supabase anon key | | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Yes | Supabase anon key |
| `SUPABASE_SERVICE_ROLE_KEY` | Yes | Admin operations | | `SUPABASE_SERVICE_ROLE_KEY` | Yes | Admin operations |
| `SUPABASE_JWT_SECRET` | Yes | JWT verification | | `SECRETS_ENCRYPTION_KEY` | Yes | AES-256-GCM key for the secrets vault |
| `NEXT_PUBLIC_OAUTH_GITHUB` / `NEXT_PUBLIC_OAUTH_GOOGLE` | No | Enable social sign-in buttons |
`.env.example` and `docs/DEPLOYMENT.md` are the authoritative env list. (`SUPABASE_JWT_SECRET` is not used by the app and was removed from this table.)
--- ---
@@ -591,11 +601,10 @@ self-mark host. Surfaced as the dashboard "Who's here" panel, the
## 13. Known Limitations ## 13. Known Limitations
1. **No test suite** - Unit, integration, and E2E tests needed 1. **Test suite** - Extensive Vitest unit coverage of pure logic (`__tests__/`, run with `pnpm test`) plus Playwright smoke e2e (`e2e/`). Gap: server-action orchestration is not unit-tested (no Supabase mock harness); e2e is mostly page-render checks, not full write/RLS flows.
2. **Payment integrations** - UI only, no actual OAuth/API 2. **Payment integrations** - Manual import/CSV reconciliation is the primary path. A PayPal sync endpoint exists (`app/api/paypal/sync/route.ts`); Stripe/other live APIs are not integrated. See `docs/` and the Payments module section.
3. **Import feature** - UI only, no file processing 3. **Social auth** - GitHub/Google sign-in is wired, gated by the `NEXT_PUBLIC_OAUTH_GITHUB`/`NEXT_PUBLIC_OAUTH_GOOGLE` env flags.
4. **Database connector** - UI only, not implemented 4. **Webhooks** - The HMAC signing contract and secret rotation exist; per-event delivery is not implemented (see `docs/WEBHOOKS.md`).
5. **Social auth** - GitHub/Google buttons present but not wired 5. **Email notifications** - Not implemented.
6. **Webhook endpoint** - URL shown but no actual endpoint 6. **Search** - Client-side filtering only, no server-side full-text search.
7. **Email notifications** - Not implemented 7. **Single space per user** - The auth resolver assumes one active membership per user; a user in 2+ spaces is not supported (no space switcher). Fails closed.
8. **Search** - Client-side only, no full-text search
+7 -6
View File
@@ -1,12 +1,13 @@
# Hackerspace.sh - Database Schema Reference # Hackerspace.sh - Database Schema Reference
> **Last Updated**: 2026-05-15 > **Last Updated**: 2026-05-17
> **Database**: PostgreSQL via self-hosted Supabase > **Database**: PostgreSQL via self-hosted Supabase
> **Tables**: ~30 > **Tables**: ~43
> **Source of Truth**: `scripts/schema.sql` (canonical, idempotent); numbered > **Source of Truth**: `scripts/schema.sql` (canonical, idempotent); numbered
> migrations `scripts/0NN_*.sql` upgrade existing deployments. The 13-table > migrations `scripts/0NN_*.sql` (through **038**) upgrade existing
> reference below is the original baseline; tables added by migrations > deployments. The 13-table reference below is the original baseline; tables
> 014-025 are summarized in the "Migrations 014-025" section at the end. > added by migrations 014-038 are summarized in the migrations section at the
> end. `DB_SCHEMA_MAP.md` has the per-table quick map.
--- ---
@@ -686,7 +687,7 @@ CREATE INDEX idx_activity_space ON activity_log(space_id, created_at);
--- ---
## Migrations 014-035 (additions since the 13-table baseline) ## Migrations 014-038 (additions since the 13-table baseline)
`scripts/schema.sql` is the canonical idempotent schema; each numbered `scripts/schema.sql` is the canonical idempotent schema; each numbered
migration is mirrored as a section in it. Tables/columns added: migration is mirrored as a section in it. Tables/columns added: