Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .claude/hooks/check-migration-guard.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash
# Hook: Prevent editing existing migration files (forward-only migrations)
# Rule: "Forward-only. Idempotent." — CLAUDE.md
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty')
[ -z "$FILE_PATH" ] && exit 0
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Only check migration files
case "$FILE_PATH" in
*migrations/*.sql|*migrations/*.ts)
# Allow creating NEW migration files (Write tool with no existing file)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
if [ "$TOOL_NAME" = "Write" ] && [ ! -f "$FILE_PATH" ]; then
exit 0
fi
# Block editing existing migration files
if [ -f "$FILE_PATH" ]; then
echo "BLOCKED: Cannot edit existing migration file: $(basename "$FILE_PATH")" >&2
echo "Rule: Migrations are forward-only. Create a new migration instead." >&2
echo "Use: npm run db:generate" >&2
exit 2
fi
;;
esac

exit 0
71 changes: 71 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# NeoBoard — Environment Variables
# Copy to app/.env.local and fill in values.
# For dev setup, scripts/setup.sh generates these automatically.

# PostgreSQL connection (required)
DATABASE_URL=postgresql://neoboard:neoboard@localhost:5432/neoboard

# 32-byte hex key for AES-256-GCM credential encryption (required)
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# WARNING: losing this key makes all stored credentials unrecoverable.
ENCRYPTION_KEY=

# Previous encryption key — set this when rotating ENCRYPTION_KEY (optional)
# Rotation flow: 1) copy current ENCRYPTION_KEY to ENCRYPTION_KEY_OLD,
# 2) generate and set a new ENCRYPTION_KEY, 3) restart the app,
# 4) call POST /api/admin/rotate-key (admin only) to re-encrypt all credentials,
# 5) remove ENCRYPTION_KEY_OLD after successful rotation.
# ENCRYPTION_KEY_OLD=

# Auth.js session secret (required)
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
NEXTAUTH_SECRET=

# Application URL (required)
NEXTAUTH_URL=http://localhost:3000

# One-time token for creating the first admin account via /signup (optional, dev only)
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
ADMIN_BOOTSTRAP_TOKEN=

# HMAC secret for API key hashing — required if using API keys (optional)
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
API_KEY_HMAC_SECRET=

# Self-registration toggle — set to "false" to disable /signup (optional, default: true)
# REGISTRATION_ENABLED=true

# Tenant ID — defaults to "default" if unset (optional)
# TENANT_ID=default

# Session max age in seconds — defaults to 28800 (8 hours) if unset (optional)
# SESSION_MAX_AGE=28800

# Log level — one of: fatal, error, warn, info, debug, trace (optional, default: info)
# LOG_LEVEL=info

# Per-user query rate limit — max queries per minute per user (optional, default: 60)
# QUERY_RATE_LIMIT=60

# ── SSO / OIDC (optional) ────────────────────────────────────────────────────
# Set all three required vars to enable a single OIDC provider via env.
# Requires NEOBOARD_EDITION=enterprise.
# For multiple providers, use the Admin UI (Settings > Authentication).

# Required (all four must be set to activate SSO)
# NEOBOARD_EDITION=enterprise
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
# OIDC_ISSUER=https://myorg.okta.com
# OIDC_CLIENT_ID=neoboard
# OIDC_CLIENT_SECRET=your-client-secret

# Optional
# OIDC_DISPLAY_NAME=Company SSO
# OIDC_SCOPES=openid profile email
# OIDC_CLAIM_KEY=groups
# OIDC_ADMIN_VALUE=neoboard-admins
# OIDC_CREATOR_VALUE=neoboard-editors
# OIDC_READER_VALUE=neoboard-viewers
# OIDC_AUTO_PROVISION=true
# OIDC_DEFAULT_ROLE=creator
# OIDC_ENFORCE_SSO=false

3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "enterprise"]
path = enterprise
url = https://github.com/alfredo1996/neoboard-enterprise.git
72 changes: 72 additions & 0 deletions docker/docker-compose.prod-full.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Full-stack NeoBoard: app + PostgreSQL in a single compose.
# For single-server deployments where you don't have an external PostgreSQL.
#
# Usage:
# cp ../.env.example .env
# # Fill in ENCRYPTION_KEY, NEXTAUTH_SECRET, NEXTAUTH_URL
# docker compose -f docker/docker-compose.prod-full.yml up -d
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-neoboard}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-neoboard}
POSTGRES_DB: ${POSTGRES_DB:-neoboard}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-neoboard}"]
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
restart: unless-stopped

neoboard:
build:
context: ..
dockerfile: Dockerfile
ports:
- "${PORT:-3000}:3000"
environment:
DATABASE_URL: postgresql://${POSTGRES_USER:-neoboard}:${POSTGRES_PASSWORD:-neoboard}@postgres:5432/${POSTGRES_DB:-neoboard}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000}
FORCE_HTTPS: ${FORCE_HTTPS:-false}
API_KEY_HMAC_SECRET: ${API_KEY_HMAC_SECRET:-}
depends_on:
postgres:
condition: service_healthy
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 256M
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
healthcheck:
test: ["CMD", "wget", "-q", "-O-", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
restart: unless-stopped

volumes:
pgdata:
1 change: 1 addition & 0 deletions enterprise
Submodule enterprise added at 9461e0