You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs/app_spec.txt - Canonical app specification and architecture overview
docs/architecture/README.md - Architecture docs index
docs/security.md - Current auth and browser security baseline
docs/testing.md - Testing guide
Internal CLI (BHono)
Use the internal CLI to scaffold new apps:
# Recommended (no global install)
pnpm dlx @etus/bhono <project-name># scaffolder shortcut (delegates to @etus/bhono)
npx @etus/create-bhono <project-name>
If the CLI cannot find local templates, it will clone the default Bhono template repo. You can override this by setting BHONO_TEMPLATE_REPO (or ETUS_TEMPLATE_REPO) to a git URL.
Database Schema
Core Tables
-- Users provisioned by @etus/auth after OAuth callback
auth_users (
id TEXTPRIMARY KEY,
gateway_user_id TEXT,
email TEXTNOT NULL UNIQUE,
name TEXT,
picture TEXT,
role TEXTNOT NULL DEFAULT 'guest',
status TEXTNOT NULL DEFAULT 'pending',
invited_by TEXT,
created_at TEXT,
last_login_at TEXT
)
-- Session references for listing/revocation; full session payload lives in KV
auth_sessions (
id TEXTPRIMARY KEY,
user_id TEXTNOT NULLREFERENCES auth_users(id) ON DELETE CASCADE,
ip TEXT,
user_agent TEXT,
last_active_at INTEGER,
expires_at INTEGER,
created_at INTEGER
)
-- Accounts and memberships for multi-tenant workspaces
auth_accounts (
id TEXTPRIMARY KEY,
name TEXTNOT NULL,
slug TEXT UNIQUE,
owner_id TEXTNOT NULL,
created_at TEXT,
updated_at TEXT
)
auth_memberships (
id TEXTPRIMARY KEY,
account_id TEXTNOT NULL,
user_id TEXTNOT NULL,
role TEXTNOT NULL DEFAULT 'guest',
status TEXTNOT NULL DEFAULT 'active',
invited_by TEXT,
invited_at TEXT,
joined_at TEXT,
created_at TEXT,
UNIQUE(account_id, user_id)
)
-- Invitations and audit logs
auth_invitations (
id TEXTPRIMARY KEY,
account_id TEXTNOT NULL,
email TEXTNOT NULL,
role TEXTNOT NULL DEFAULT 'guest',
invited_by TEXTNOT NULL,
token TEXTNOT NULL UNIQUE,
expires_at TEXTNOT NULL,
accepted_at TEXT,
created_at TEXT
)
auth_audit_logs (
id TEXTPRIMARY KEY,
event_type TEXTNOT NULL,
actor_id TEXT,
actor_email TEXT,
target_id TEXT,
target_type TEXT,
account_id TEXT,
ip TEXT,
user_agent TEXT,
metadata TEXT,
created_at TEXT
)
# Run server tests in watch mode
pnpm test# Run server tests once with coverage
pnpm test:unit:server
# Run client tests with coverage
pnpm test:unit:client
# Run all unit tests
pnpm test:unit
Integration Tests
# Run integration tests
pnpm test:integration
# Run integration tests in watch mode
pnpm test:integration:watch
End-to-End Tests
# Run all E2E tests
pnpm test:e2e
# Interactive UI mode
pnpm test:e2e:ui
# Visible browser
pnpm test:e2e:headed
# Debug mode
pnpm test:e2e:debug
# View HTML report
pnpm test:e2e:report
# Run specific tests by tag
npx playwright test --grep "@smoke"
npx playwright test --grep "@critical"
npx playwright test --grep "@visual"
npx playwright test --grep "@a11y"
Production Testing
Test against the live production deployment with OAuth authentication:
# Step 1: Capture OAuth session (opens browser for manual login)
pnpm test:e2e:prod:auth
# Step 2: Run tests against production
pnpm test:e2e:prod
# Or with custom URL
BASE_URL=https://your-app.workers.dev pnpm test:e2e
Test Categories
Tag
Description
@smoke
Basic smoke tests
@critical
Critical paths (all browsers)
@crud
CRUD operations
@mobile
Mobile responsive
@visual
Visual regression
@a11y
Accessibility
@api
API integration
Deployment
Cloudflare Workers
# Build the application
pnpm build
# Deploy to Cloudflare
wrangler deploy --config config/wrangler.json
import{Button}from'@/components/ui/button'// Clientimport{userSchema}from'@shared/schemas'// Sharedimport{createUser}from'@server/services/users'// Server
Scripts Reference
Script
Description
pnpm dev
Start development server
pnpm build
Build for production
pnpm preview
Preview production build
pnpm lint
Run ESLint
pnpm typecheck
Run TypeScript checks
Database
pnpm db:schema:local
Apply schema.sql locally
pnpm db:schema:remote
Apply schema.sql remotely
pnpm db:seed
Generate seed.sql
pnpm db:seed:local
Generate + seed locally
pnpm db:reset:local
Apply schema + seed locally
pnpm cf-typegen
Generate Cloudflare types
API
pnpm api:spec
Generate OpenAPI spec (docs/openapi.json)
pnpm api:types
Generate TypeScript types from OpenAPI
Testing
pnpm test
Run backend unit tests (watch)
pnpm test:unit:server
Backend tests with coverage
pnpm test:unit:client
Frontend tests with coverage
pnpm test:integration
Integration tests
pnpm test:e2e
Run E2E tests
pnpm test:e2e:ui
E2E interactive mode
pnpm test:e2e:prod
E2E against production
pnpm test:e2e:prod:auth
Capture OAuth session
Deployment
wrangler deploy --config config/wrangler.json
Deploy to Cloudflare
Security
Built-in Protections
Session Auth: @etus/auth issues HTTP-only cookies backed by KV sessions and D1 session references
CSRF: mutating requests require a trusted Origin/Referer; JSON endpoints must use JSON content type
CORS: credentialed CORS uses explicit origins from APP_URL and CORS_ORIGINS; wildcard origins are rejected in production
XSS: React escaping, no client-side raw HTML sinks, CSP with nonced scripts, frame-ancestors 'none', nosniff, and referrer policy
Session Hijacking: secure cookie flags on HTTPS and session fingerprint validation
SQL Injection: Parameterized queries via SQL helpers
Rate Limiting: In-memory store with configurable limits per route
Payload DoS: request bodies are capped before route parsing; direct R2 uploads use a larger explicit cap
Frontend Secrets: client guardrails reject auth token storage, dangerous DOM sinks, and non-public env exposure
Audit Logging
Auth, user lifecycle, account, membership, invitation and session events are logged by @etus/auth when audit.enabled=true: