Skip to content

hechi/crewcaptain

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

229 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CrewCaptain

License: AGPL-3.0

A self-hosted, privacy-first manager workspace for organizing people context, 1:1 history, development goals, action items, and kudos. CrewCaptain is not HR software — it's a private cockpit for people-centric leadership.

Heads up — please read before relying on this. CrewCaptain was built with the help of AI (Kiro) and is maintained by a single developer in their spare time. I have every intention of fixing bugs and addressing issues, but my free time is limited, so there are no guarantees on response or fix timelines. Treat this as a best-effort, as-is open-source project (see LICENSE — no warranty) and review the code yourself before depending on it for anything important.


Features

Implemented

  • Person Directory — Full CRUD for team members with name, preferred name, role title, timezone, start date, email, and tags
  • Morale Tracking — Visual morale indicators (Green/Yellow/Red/Unknown) per person with optional notes
  • Pinned Remember Items — Add, remove, and reorder quick-reference notes per person
  • Sticky Notes — Visual sticky-note cards replacing the basic pinned items list. Each note has a color (6 options: cyan, purple, green, amber, pink, slate), optional tag label, sensitive flag, and supports inline edit, drag-and-drop reorder, and delete with undo (10s toast). Person detail shows a card grid with truncation at 100 chars; People list shows up to 2 non-sensitive previews per card. Starter templates for quick capture (Family, Docs, Link, Life event, Manager). Markdown export includes sticky notes with sensitive content masked. Soft limit of 10 notes with friendly warning.
  • Filtering & Pagination — Filter people by tag or morale status with paginated results
  • At-a-Glance Summary — Person detail includes last 1:1 date, open action items count, and active PDP goals (placeholder)
  • OIDC Authentication — Secure login via authentik (OAuth2/OIDC) with automatic user provisioning
  • Data Isolation — All queries scoped by authenticated user (manager) — no cross-user data access
  • Frontend UI — People list, person detail, create person pages with filter bar, morale indicators, and empty states
  • Cyberpunk-Lite Design System — Dark-first UI with electric cyan/neon violet accents, JetBrains Mono headings, glassmorphism cards, glow effects, Inter body text, CSS custom properties design tokens, and consistent navigation
  • 1:1 Entry Management — Full-stack series configuration (cadence + template), entry CRUD with agenda items, Markdown notes, outcomes, sensitive flag, paginated timeline, template prefill, and person at-a-glance last 1:1 date. Prep Notes Panel: collapsible section on the 1:1 entry page (create + edit) that surfaces INBOX quick notes assigned to the person as "talking points to discuss." One-click "Add to Agenda" attaches the note to the 1:1 entry; "Dismiss" archives it. Hidden when empty.
  • Action Items — Create, track, complete, and cancel follow-ups from 1:1s with per-person and cross-person views, overdue filtering, owner type (manager/person), due dates, and status transitions (OPEN → DONE, OPEN → CANCELED). Full frontend with action items tab on person detail, status filter, inline create/edit forms, and cyberpunk-themed components. Inline action items section on the 1:1 entry page allows quick-adding action items during a session (auto-linked to the entry), viewing existing open items for the person, and marking items done — all without leaving the 1:1 page.
  • PDP Goal Tracking — Personal development plans per person with goals (title, description, target date), status transitions (ACTIVE → ACHIEVED/PAUSED/DROPPED, PAUSED → ACTIVE), timestamped progress updates with sensitive flag, and full frontend with PDP goals tab, status filter, inline create/edit forms, and cyberpunk-themed components.
  • Kudos / Recognition — Record positive feedback and achievements per person with date, Markdown text, and optional tags (e.g., "impact", "collaboration"). Full frontend with Kudos tab on person detail, inline create form, and delete. Immutable entries (create + delete only).
  • Quick Notes (Inbox) — Global quick capture for thoughts, follow-ups, and observations. Notes can be unassigned (inbox), assigned to a person, or self-assigned (personal notes for the manager). Status workflow: INBOX → ATTACHED (to 1:1) / CONVERTED (to action item) / ARCHIVED. Supports sensitive flag. Self-assigned notes are accessible via "My Notes" in the user menu. Invariant: a note cannot be both self-assigned and assigned to a person — assigning to a person clears the self-assigned flag. Full frontend with dedicated Quick Notes page, My Notes page, status filter, and inline create form. Quick Note Overlay — Global floating overlay accessible from any page via Ctrl+Shift+Q or the floating action button (bottom-right). Cyberpunk glassmorphism design with neon glow fade-in animation, scan-line texture, and slide-up entrance. Supports Ctrl+Enter to save. Auto-closes after successful capture. Dismissible via Escape, backdrop click, or cancel button. Respects prefers-reduced-motion.
  • Dashboard — At-a-glance overview showing overdue action items, due-soon items, stale 1:1 reminders (based on cadence), and upcoming work anniversaries. Configurable lookahead windows for due-soon (default 3 days) and anniversaries (default 30 days).
  • Sensitive Content Encryption — Application-level AES-256-GCM encryption for sensitive text fields at rest. When ENCRYPTION_KEY is configured, all content marked sensitive=true (1:1 notes/outcomes, quick notes, PDP updates) is encrypted before storage and decrypted on read. Graceful fallback: without a key, the system operates normally with plaintext storage. Supports legacy unencrypted data migration (reads both encrypted and unencrypted content).
  • In-App Notifications — Scheduled notification generation for overdue action items, due-soon items (configurable threshold, default 3 days), stale 1:1 reminders (based on cadence), and upcoming work anniversaries (7-day lookahead). Notification center with bell icon in navigation, unread badge, mark-as-read (individual and bulk), and dedicated notifications page with pagination and unread filter. Deduplication prevents duplicate notifications within 24 hours. Scheduler runs hourly by default (configurable via cron expression).
  • Full-Text Search — Search across all manager data (people, 1:1 notes, quick notes, action items, PDP goals, kudos) using PostgreSQL full-text search with GIN indexes and relevance ranking. Type filters, pagination, and sensitive content protection (sensitive snippets hidden in results). Dedicated search page with real-time URL state and navigation link.
  • Per-Person Markdown Export — Export all data for a person as a structured Markdown file: profile summary, pinned remember items, morale, 1:1 history (reverse chronological), action items (grouped by status), PDP goals with progress updates, and kudos. Optional date range filter. Sensitive content is marked but not exposed. Download via Export button on person detail page.
  • Gamification & Engagement — Dashboard gamification elements for engagement: animated progress ring for PDP goal completion percentage, 1:1 streak counter (consecutive weeks with meetings), achievement badges for milestones (first 1:1, 10 action items closed, etc.), and activity heatmap (contribution-graph style). Micro-animation on task completion (checkmark with glow burst). All animations respect prefers-reduced-motion.
  • User Settings — Per-user persistent settings page with: theme selection (dark/light), dashboard reminder thresholds (due-soon days, stale 1:1 days, anniversary lookahead), notification type toggles (overdue, due-soon, stale 1:1, anniversary), and achievement visibility toggle. Settings are stored in the database and respected by the notification scheduler and dashboard.
  • Light Theme — Full light theme alternative to the default cyberpunk dark theme. Clean surfaces, teal/purple accents, proper contrast ratios, and subtle shadows instead of glows. Toggled via Settings page.
  • Review Packet Generator — Generate structured review/performance summary documents for a person over a configurable date range. Includes executive summary with statistics (1:1 count, action item completion rate, PDP goal progress, kudos count), morale status, detailed 1:1 meeting history, action items grouped by status, PDP goals with progress updates, and kudos with tag summary. Sensitive content is excluded. Download as Markdown via "Review Packet" button on person detail page.
  • Bulk Import (CSV) — Import multiple people at once from a CSV file. Supports columns: name (required), preferred_name, role_title, timezone, start_date (YYYY-MM-DD), email, tags (pipe-separated). Preview before import, per-row error reporting, max 500 rows per import. Accessible via "Import CSV" button on the People list page.
  • Soft-Delete + Restore — Deleting a person moves them to trash (soft-delete) instead of permanently removing them. Trash page shows all deleted people with restore and permanent delete capability. All queries automatically exclude soft-deleted records. Data isolation enforced on trash operations. Permanent delete requires confirmation and cascades to all associated data (1:1 entries, action items, PDP goals, kudos).
  • Audit Log — Records key actions (create, update, delete, restore) across all entities for the manager's own traceability. Paginated audit log page with entity type and action filters. All entries scoped by userId. Accessible via user menu in navigation.
  • Workspaces — Lightweight organizational containers for grouping people (e.g., "My Team", "Mentees", "Skip-levels"). A workspace belongs to a single manager (private, no sharing). A person belongs to one workspace (optional). Opt-in: if no workspaces exist, everything works as before. Includes workspace CRUD, person-to-workspace assignment, workspace filter on People list, and management page accessible via user menu.
  • Landing Page — Modern, high-converting landing page with cyberpunk-lite dark theme. Hero section with HUD visual motif, feature cards with glassmorphism, dedicated AI features section highlighting all AI capabilities (agenda generation, outcome extraction, performance narrative, kudos refinement, SMART goal check, and configurable AI settings), interactive screenshot showcase (tabbed gallery with 11 views including AI-powered features, quick capture, and settings), 3-step deployment guide, privacy/self-hosted messaging, and dual CTA sections. Fully responsive, accessible (WCAG AA), respects prefers-reduced-motion. Authenticated users are redirected to the dashboard.
  • Prometheus Metrics — Exposes application metrics at /actuator/prometheus for Prometheus scraping. Secured with a bearer token (METRICS_TOKEN). Includes JVM metrics, HTTP request metrics, HikariCP connection pool stats, and custom 1:1 metrics (total entries, entries in last 7 days). Health endpoint at /actuator/health remains unauthenticated for Docker healthchecks.
  • AI-Powered 1:1 Prep Assistant — Optional AI assistant that synthesizes person-specific context (recent 1:1 notes, open action items, active PDP goals, recent kudos) and generates 3-5 suggested agenda items for the next meeting. Configurable per-user in Settings: API Base URL (any OpenAI-compatible endpoint — Ollama, LiteLLM, OpenAI, etc.), API Key, Model Name, and Privacy Mode toggle. When Privacy Mode is ON, content marked sensitive=true is never sent to the LLM. Graceful failure: if the API is unreachable, shows an inline error without breaking the 1:1 page. One-click add suggestions to the agenda. Cyberpunk-themed UI with glow burst animation on completion and pulse loading state. Respects prefers-reduced-motion.
  • AI Performance Narrative Generator — Generate AI-powered performance review narratives from historical data. Aggregates kudos (with tags), PDP goals (with status and non-sensitive updates), 1:1 outcomes, and action item completion stats within a configurable date range. Sends context to the user's configured LLM with a leadership-coach system prompt. Three writing styles: Narrative (3 paragraphs), Bullet Points (structured sections), Concise (1 paragraph). Privacy Mode respected — sensitive content excluded unless disabled. Result displayed in an editable textarea with copy-to-clipboard. Button only visible when AI is enabled in Settings. Cyberpunk pulse animation during generation.
  • AI Coaching & Feedback Refinement — AI-powered coaching tools integrated into Kudos and PDP Goals. Kudos Refinement: "Refine" button on the Kudos form sends the draft to the LLM using the SBI (Situation-Behavior-Impact) framework, returning a polished version in a comparison view (Apply/Keep Original). PDP Goal SMART Check: "SMART Check" button on the PDP Goal form evaluates the goal against SMART criteria and suggests an improved title and description. Customizable Prompts: All AI system prompts (Kudos Refinement, PDP Optimization, Agenda Prep, Narrative) are configurable per-user in Settings under the "AI Prompts" section, with "Reset to Default" buttons. Existing AI features (1:1 Prep, Narrative Generator) now use the user's custom prompts when set.
  • AI Outcome Extractor — Post-meeting productivity tool that parses 1:1 entry notes using the configured LLM to extract action items and key decisions. Identifies tasks for both Manager and Direct Report with inferred due dates. Presents results in a review modal where items can be edited, toggled, or deselected before bulk-applying. Action items are created with originatingEntryId linking them to the source entry. Decisions are appended to the entry's Outcomes field. Duplicate detection: items matching existing action item titles are flagged and pre-unchecked. Privacy: disabled for sensitive entries when AI Privacy Mode is ON. Custom prompt configurable in Settings. Cyberpunk-themed modal with owner-type color coding (cyan for Manager, violet for Person).
  • AI Strategic Trend Radar — Diagnostic tool that analyzes 90 days of team member data (1:1 outcomes, action items, PDP progress, kudos) to surface long-term patterns and momentum shifts. Evaluates four dimensions: Sentiment/Morale Drift, Work/Growth Balance, Recognition Velocity, and Meeting Efficacy. Each insight includes a Confidence Score (0-100%) based on data volume and recency: Low (<40%, insufficient data), Moderate (40-75%, some signal), High (>75%, strong signal). Minimum 2 meetings required to generate insights; shows "Scanning horizon..." empty state otherwise. Privacy Mode respected — outcomes excluded when enabled. Custom system prompt configurable in Settings. Cyberpunk glassmorphism insight cards with neon confidence gauges and dimension icons. New "✦ Insights" tab on Person Detail page (conditionally visible when AI enabled).
  • Strategy Hub — Strategic layer for managers to define high-level objectives and visualize team PDP goal alignment. Create strategy goals with title, description, target date, and sensitive flag. Link PDP goals to strategy goals to track alignment. Alignment scoring shows percentage of active PDP goals contributing to each strategy goal. Gap analysis panel highlights unlinked PDP goals and strategy goals without contributors. Full CRUD with status transitions (ACTIVE → ACHIEVED/DROPPED). AES-256-GCM encryption for sensitive strategy goals. Full-text search integration with GIN index. Comprehensive audit logging for all CRUD and link/unlink operations.
  • Unified Triage Queue — Centralized actionable inbox aggregating overdue action items, due-soon items, stale 1:1 reminders, and upcoming work anniversaries into a single prioritized list. Sorted by criticality (Overdue > Due Soon > Stale > Informational), then chronologically. Filtering by scope (All/Mine), item type, workspace, and person. Vim-inspired keyboard navigation (j/k to move, d=done, c=cancel, s=snooze, a=add to 1:1, q=quick note, r=reassign, t=set due, Enter=peek drawer). Full InlineActionMenu with Done, Cancel, Snooze (1d/3d/7d submenu), Reassign Owner, Set Due Date (inline date picker), Add to 1:1, Save as Quick Note. QuickPeekDrawer (Enter key or peek icon) shows person context: morale dot, role, last 1:1 summary, open action items, and recent kudos — all without navigating away. Snooze support hides items temporarily. Toggle Owner switches between Manager/Person. AI "Next Best Action" hints (one-sentence suggestions from configured LLM, privacy mode respected, configurable prompt in Settings). Sensitive content masked in list. Workspace chip shown on rows. Glassmorphism card UI with cyberpunk glow ring on selected row. Global shortcut Cmd/Ctrl+J focuses the queue. Empty state with "You're all clear" messaging. Accessible via Navigation bar or /triage route.
  • AI Command Terminal — Natural language command overlay for creating action items, kudos, quick notes, and 1:1 entries via AI-powered parsing. Accessible via Cmd/Ctrl+K or floating action button (purple ⌘ icon, positioned next to Quick Note FAB). The terminal sends user input to the configured LLM (Ollama, OpenAI-compatible) with a structured JSON system prompt and the person directory context, then parses the response into typed commands. Supports intents: create_action_item, create_kudo, create_quick_note, create_one_on_one_entry. For 1:1 entries, the AI extracts meeting notes and a meeting date (defaulting to today if unspecified). Two execution modes: Standard (Confirm & Save) shows a preview card requiring manual confirmation, Auto-Execute (configurable in Settings) bypasses the preview and instantly executes with a 10-second undo toast. Privacy Mode: if enabled and the AI detects sensitive content, an explicit warning is displayed before execution. Sensitive items are routed through the AES-256-GCM encryption pipeline. Built-in help command (type "help" anytime to redisplay available commands without calling the AI). Cyberpunk glassmorphism terminal panel with slide-up animation, violet accent theme, continuous scrolling chat interface, scan-line texture, and monospace terminal aesthetic. Respects prefers-reduced-motion. Only visible when AI is enabled and configured in User Settings. Backend: POST /api/v1/ai/command (parse command), GET /api/v1/ai/command/directory (person directory for micro-context injection). Custom system prompt configurable in Settings as "Command Terminal Prompt".
  • AI Admin/Team Defaults — Admins can provide team-wide AI configuration via environment variables (AI_DEFAULT_BASE_URL, AI_DEFAULT_API_KEY, AI_DEFAULT_MODEL). Users who haven't configured their own AI settings automatically use the team defaults. Users who configure their own AI server/model in Settings override the team defaults. The Settings page shows a badge indicating the active config source ("AI available via team defaults" or "Using your personal AI config"). GET /api/v1/settings/ai-status returns the resolved AI availability and config source. All AI features (Command Terminal, Prep Assistant, Narrative, Coaching, Outcome Extractor, Trend Radar, Link Suggestions, Triage Hints) are shown whenever AI is effectively available — i.e. when either the user has their own config or admin team defaults are set. The frontend gates feature visibility on the resolved aiAvailable flag (not the user's personal aiEnabled toggle), so setting only the admin defaults lights up the features for everyone. When neither source is configured, all AI features are hidden.

Planned

  • (none currently)

Tech Stack

Layer Technology
Backend Kotlin + Spring Boot 3.3.5 (Hexagonal/DDD)
Frontend Next.js 14 + React 18 + Auth.js (OIDC)
Database PostgreSQL 16
Auth OAuth2 / OIDC via authentik
Deployment Docker Compose
API Style REST + JSON
Migrations Flyway
Testing JUnit 5 + Kotest + Testcontainers (backend), Jest + React Testing Library (frontend)

Prerequisites

  • Docker 24+ and Docker Compose v2+
  • Java 21 (for local backend development)
  • Node.js 20+ and npm (for local frontend development)
  • authentik instance (or any OIDC provider) for authentication — or use the bundled local authentik via the dev-auth overlay (see Local OAuth (Authentik))

Quick Start

Try it in one command (all-in-one demo)

Just want to kick the tyres? docker-compose.full-demo.yml is a single, self-contained file that runs the entire stack — CrewCaptain, PostgreSQL, a preconfigured authentik (login), and a local Ollama (AI) — with baked-in demo defaults. No repo checkout, no .env, no identity provider setup.

You only need this one file. Download it, then:

# 1. One-time: add this to your hosts file so the browser and containers
#    resolve authentik at the same hostname (keeps the OIDC issuer consistent).
#    Linux/macOS: /etc/hosts   —   Windows: C:\Windows\System32\drivers\etc\hosts
echo "127.0.0.1 authentik" | sudo tee -a /etc/hosts

# 2. Start everything (first boot pulls images + a ~1 GB AI model)
docker compose -f docker-compose.full-demo.yml up

# 3. Open http://localhost:3000 and sign in with:
#       username: demo
#       password: demo12345

First boot takes a minute or two while authentik migrates its database and Ollama downloads the model; the app stays up while that happens, and AI/login light up once they finish.

⚠️ Demo only. This file ships well-known, committed secrets so it works out of the box. Never expose it to the internet or use it in production — use docker-compose.yml with your own secrets and a real OIDC provider instead.

Using Docker Compose (Production)

The production docker-compose.yml pulls pre-built images from the container registry:

# 1. Clone the repository
git clone https://github.com/hechi/crewcaptain.git   # or: git@github.com:hechi/crewcaptain.git
cd crewcaptain

# 2. Configure environment variables
cp .env.example .env
# Edit .env with your actual values (database credentials, OIDC settings)

# 3. Start the full stack (pulls images from registry)
docker compose up -d

# 4. Access the application
# Frontend: http://localhost:3000
# API:      http://localhost:8080

Images:

  • ghcr.io/hechi/crewcaptain/api:latest
  • ghcr.io/hechi/crewcaptain/frontend:latest

These public images are built and published by the GitHub Actions pipeline on every push to main.

Using Docker Compose (Local Development)

The docker-compose.override.yml adds build directives and dev tooling. When present, docker compose up will build from source:

# Copy the example override file
cp docker-compose.override.example.yml docker-compose.override.yml

# Build and start with local source (override is auto-loaded)
docker compose up --build

The override exposes the database port (5432), mounts source volumes for hot-reload, and sets development environment variables.

Local OAuth (Authentik)

CrewCaptain authenticates via OIDC, so trying it out normally requires an external identity provider. For development and evaluation, the docker-compose.dev-auth.yml overlay runs a self-contained authentik instance that is preconfigured with a demo user and a ready-to-use OIDC application — no clicking through setup wizards.

⚠️ Development only. This overlay ships well-known, committed credentials (demo / demo12345) via a blueprint. Never enable it in production — use your own real authentik (or any OIDC provider) there.

One-time setup — add this line to your /etc/hosts so the browser and the containers resolve authentik at the same hostname (this is what keeps the OIDC token issuer consistent):

127.0.0.1 authentik

Start the stack with the overlay:

docker compose -f docker-compose.yml -f docker-compose.dev-auth.yml up

First boot takes ~30–60s while authentik migrates its database and applies the bootstrap blueprint. Then:

The demo OIDC application (client_id: crewcaptain), the demo user, and the provider are all created automatically by authentik/blueprints/crewcaptain-dev.yaml. Default secrets can be overridden via the AUTHENTIK_* variables documented in .env.example.

Note: The frontend container sets AUTH_TRUST_HOST=true (in docker-compose.yml). Auth.js v5 requires this when running self-hosted behind Docker/a proxy; without it you'll see UntrustedHost: Host must be trusted. If you change compose env, recreate the container with docker compose ... up -d --force-recreate frontend so it's picked up.

Local AI (Ollama)

CrewCaptain's AI features (1:1 prep suggestions, summaries, performance narratives, etc.) talk to any OpenAI-compatible endpoint. For development and evaluation, the docker-compose.ai.yml overlay runs a self-contained Ollama instance that automatically pulls a small model on first start and wires it in as the team-wide AI default — so the AI features work out of the box with no external API key.

By default it pulls qwen2.5:1.5b (~1 GB), a small instruction-following model that's enough to demonstrate summaries and suggestions and runs on CPU. It's a demo-grade model — fine for kicking the tyres, not for production-quality output.

Start the stack with the overlay:

docker compose -f docker-compose.yml -f docker-compose.ai.yml up

Or bring up local auth and local AI together:

docker compose -f docker-compose.yml \
               -f docker-compose.dev-auth.yml \
               -f docker-compose.ai.yml up

On first start the ollama-init helper downloads the model (~1 GB, one-time — stored in the ollama-models volume). AI calls fail gracefully until the pull finishes, so nothing crashes while it downloads. Once ready, AI features light up automatically for all users (via AI_DEFAULT_*); users can still override with their own provider in Settings.

Tuning:

  • Smaller/faster model: OLLAMA_MODEL=gemma3:1b docker compose -f docker-compose.yml -f docker-compose.ai.yml up
  • GPU acceleration: uncomment the deploy.resources block in docker-compose.ai.yml (requires the NVIDIA Container Toolkit).
  • Image tag: override with OLLAMA_TAG.

⚠️ This overlay is for development/evaluation. CPU inference on a tiny model is slow and low-quality compared to a hosted model; for real deployments, point AI_DEFAULT_* (or per-user Settings) at a production-grade LLM endpoint.

Local Development

CrewCaptain uses dev.sh as the primary local development runner:

# Start the backend (requires Java 21, PostgreSQL running)
./dev.sh backend

# Start the frontend (requires Node.js 20+)
./dev.sh frontend

The script handles dependency installation, database migrations (Flyway), and starts services with hot-reload enabled.

Backend runs on http://localhost:8080 Frontend runs on http://localhost:3000


API Endpoints

All endpoints require Authorization: Bearer <jwt> header. Base path: /api/v1/

Person Directory

Method Endpoint Description
POST /api/v1/persons Create a new person
GET /api/v1/persons List persons (paginated)
GET /api/v1/persons/{id} Get person by ID
PUT /api/v1/persons/{id} Update a person
DELETE /api/v1/persons/{id} Soft-delete a person (move to trash)
POST /api/v1/persons/{id}/restore Restore a soft-deleted person
DELETE /api/v1/persons/{id}/permanent Permanently delete a soft-deleted person
GET /api/v1/persons/trash List deleted persons (paginated)
PUT /api/v1/persons/{id}/morale Set morale status
POST /api/v1/persons/{id}/remember-items Add a pinned remember item
PUT /api/v1/persons/{id}/remember-items/{itemId} Update a remember item
DELETE /api/v1/persons/{id}/remember-items/{itemId} Remove a remember item
PUT /api/v1/persons/{id}/remember-items/reorder Reorder remember items
GET /api/v1/persons/{id}/export Export person data as Markdown
GET /api/v1/persons/{id}/review-packet Generate review packet as Markdown
POST /api/v1/persons/{id}/ai-narrative Generate AI performance narrative
POST /api/v1/persons/{personId}/ai-prep Generate AI 1:1 agenda suggestions
POST /api/v1/persons/{personId}/ai-trend-radar Generate AI strategic trend insights
POST /api/v1/persons/{personId}/one-on-one-entries/{entryId}/extract-outcomes AI-extract action items and decisions from notes
POST /api/v1/persons/{personId}/one-on-one-entries/{entryId}/apply-outcomes Bulk-create extracted action items and append decisions
POST /api/v1/persons/import Bulk import persons from CSV

Export query parameters:

  • dateFrom — Optional start date filter (ISO 8601 date, e.g., 2024-01-01)
  • dateTo — Optional end date filter (ISO 8601 date, e.g., 2024-12-31)

Export response:

  • Content-Type: text/markdown; charset=UTF-8
  • Content-Disposition: attachment; filename="export.md"
  • Body: Structured Markdown with profile, remember items, morale, 1:1 history, action items, PDP goals, and kudos

Review packet query parameters (both required):

  • dateFrom — Start date of review period (ISO 8601 date, e.g., 2024-01-01)
  • dateTo — End date of review period (ISO 8601 date, e.g., 2024-06-30)

Review packet response:

  • Content-Type: text/markdown; charset=UTF-8
  • Content-Disposition: attachment; filename="review-packet.md"
  • Body: Structured Markdown with executive summary (statistics), morale, 1:1 meetings, action items (grouped by status with completion rate), PDP goals with progress, and kudos with tag summary. Sensitive content is excluded.

AI Narrative (POST /api/v1/persons/{id}/ai-narrative):

  • Request body: { "dateFrom": "2026-01-01", "dateTo": "2026-06-30" }
  • Response: { "narrative": "Generated text...", "error": null } or { "narrative": null, "error": "Error message" }
  • Requires AI to be enabled and configured in user settings
  • Respects Privacy Mode (excludes sensitive content when enabled)
  • Writing style controlled by aiWritingStyle user setting (NARRATIVE, BULLET_POINTS, CONCISE)

Bulk import (POST /api/v1/persons/import):

  • Content-Type: multipart/form-data
  • Form field: file — CSV file with header row
  • Supported CSV columns: name (required), preferred_name, role_title, timezone, start_date (YYYY-MM-DD), email, tags (pipe-separated, e.g., "engineering|senior")
  • Maximum 500 rows per import
  • Response: { "successCount": 2, "errorCount": 1, "errors": ["Row 3: Name must not be blank"] }
  • Partial success: valid rows are imported even if some rows have errors

Query parameters for the persons list endpoint:

  • page — Page number (default: 0)
  • size — Page size (default: 20)
  • tag — Filter by tag
  • morale — Filter by morale status (GREEN, YELLOW, RED, UNKNOWN)
  • workspace — Filter by workspace UUID

1:1 Entry Management

Method Endpoint Description
PUT /api/v1/persons/{personId}/one-on-one-series Create/update 1:1 series config
GET /api/v1/persons/{personId}/one-on-one-series Get 1:1 series config
POST /api/v1/persons/{personId}/one-on-one-entries Create a 1:1 entry
GET /api/v1/persons/{personId}/one-on-one-entries List 1:1 entries (paginated)
GET /api/v1/persons/{personId}/one-on-one-entries/{entryId} Get a 1:1 entry
PUT /api/v1/persons/{personId}/one-on-one-entries/{entryId} Update a 1:1 entry
DELETE /api/v1/persons/{personId}/one-on-one-entries/{entryId} Delete a 1:1 entry

1:1 Series fields:

  • cadenceType — WEEKLY, BIWEEKLY, MONTHLY, or CUSTOM
  • customIntervalDays — Required when cadenceType is CUSTOM (positive integer)
  • templateMarkdown — Markdown template to prefill new entries

1:1 Entry fields:

  • meetingDate — Required (ISO 8601 timestamp)
  • agendaItems — List of { text, checked } objects
  • notesMarkdown — Markdown notes (prefilled from template if not provided)
  • outcomesMarkdown — Markdown outcomes/decisions
  • sensitive — Boolean flag for sensitive content (default: false)

Query parameters for the entries list endpoint:

  • page — Page number (default: 0)
  • size — Page size (default: 20)

Action Items

Method Endpoint Description
POST /api/v1/persons/{personId}/action-items Create an action item
GET /api/v1/persons/{personId}/action-items List action items for a person
GET /api/v1/persons/{personId}/action-items/{actionItemId} Get an action item
PUT /api/v1/persons/{personId}/action-items/{actionItemId} Update an action item
POST /api/v1/persons/{personId}/action-items/{actionItemId}/complete Mark action item as DONE
POST /api/v1/persons/{personId}/action-items/{actionItemId}/cancel Mark action item as CANCELED
DELETE /api/v1/persons/{personId}/action-items/{actionItemId} Delete an action item
GET /api/v1/action-items List all action items (cross-person)

Action Item fields:

  • title — Required (max 500 chars)
  • description — Optional text
  • ownerType — MANAGER or PERSON (default: MANAGER)
  • dueDate — Optional date (ISO 8601 date, e.g., 2026-05-20)
  • originatingEntryId — Optional UUID linking to a 1:1 entry

Query parameters for action items list endpoints:

  • page — Page number (default: 0)
  • size — Page size (default: 20)
  • status — Filter by status (OPEN, DONE, CANCELED)
  • originatingEntryId — Filter by originating 1:1 entry UUID (per-person endpoint only)
  • overdueOnly — Only return overdue items (cross-person endpoint only, default: false)

Status transitions:

  • OPEN → DONE (via /complete)
  • OPEN → CANCELED (via /cancel)
  • No other transitions are allowed

PDP Goals (Personal Development Plans)

Method Endpoint Description
POST /api/v1/persons/{personId}/pdp-goals Create a PDP goal
GET /api/v1/persons/{personId}/pdp-goals List PDP goals for a person
GET /api/v1/persons/{personId}/pdp-goals/{goalId} Get a PDP goal
PUT /api/v1/persons/{personId}/pdp-goals/{goalId} Update a PDP goal
POST /api/v1/persons/{personId}/pdp-goals/{goalId}/achieve Mark goal as ACHIEVED
POST /api/v1/persons/{personId}/pdp-goals/{goalId}/pause Mark goal as PAUSED
POST /api/v1/persons/{personId}/pdp-goals/{goalId}/drop Mark goal as DROPPED
POST /api/v1/persons/{personId}/pdp-goals/{goalId}/resume Resume a PAUSED goal to ACTIVE
DELETE /api/v1/persons/{personId}/pdp-goals/{goalId} Delete a PDP goal
POST /api/v1/persons/{personId}/pdp-goals/{goalId}/updates Add a progress update
GET /api/v1/persons/{personId}/pdp-goals/{goalId}/updates List progress updates
DELETE /api/v1/persons/{personId}/pdp-goals/{goalId}/updates/{updateId} Delete a progress update

PDP Goal fields:

  • title — Required (max 500 chars)
  • description — Optional text
  • targetDate — Optional date (ISO 8601 date)

PDP Goal status transitions:

  • ACTIVE → ACHIEVED (via /achieve)
  • ACTIVE → PAUSED (via /pause)
  • ACTIVE → DROPPED (via /drop)
  • PAUSED → ACTIVE (via /resume)
  • No other transitions are allowed

PDP Update fields:

  • textMarkdown — Required (Markdown text)
  • sensitive — Optional boolean (default: false)

Query parameters for PDP goals list endpoint:

  • page — Page number (default: 0)
  • size — Page size (default: 20)
  • status — Filter by status (ACTIVE, ACHIEVED, PAUSED, DROPPED)

Kudos / Recognition

Method Endpoint Description
POST /api/v1/persons/{personId}/kudos Create a kudos entry
GET /api/v1/persons/{personId}/kudos List kudos for a person (paginated)
GET /api/v1/persons/{personId}/kudos/{kudosId} Get a kudos entry
DELETE /api/v1/persons/{personId}/kudos/{kudosId} Delete a kudos entry
GET /api/v1/kudos List all kudos (cross-person)

Kudos fields:

  • text — Required (Markdown text)
  • date — Optional date (ISO 8601 date, defaults to today)
  • tags — Optional list of strings (e.g., ["impact", "collaboration"])

Query parameters for kudos list endpoints:

  • page — Page number (default: 0)
  • size — Page size (default: 20)

Quick Notes (Inbox)

Method Endpoint Description
POST /api/v1/quick-notes Create a quick note
GET /api/v1/quick-notes List quick notes (paginated)
GET /api/v1/quick-notes/{quickNoteId} Get a quick note
PUT /api/v1/quick-notes/{quickNoteId} Update a quick note
DELETE /api/v1/quick-notes/{quickNoteId} Delete a quick note
POST /api/v1/quick-notes/{quickNoteId}/assign Assign to a person
POST /api/v1/quick-notes/{quickNoteId}/assign-self Assign to self (personal note)
POST /api/v1/quick-notes/{quickNoteId}/attach Attach to a 1:1 entry
POST /api/v1/quick-notes/{quickNoteId}/convert Mark as converted (to action item)
POST /api/v1/quick-notes/{quickNoteId}/archive Archive the quick note

Quick Note fields:

  • text — Required (Markdown text)
  • personId — Optional UUID to assign to a person
  • sensitive — Optional boolean (default: false)
  • selfAssigned — Optional boolean (default: false). When true, the note is a personal note for the manager. Mutually exclusive with personId.

Quick Note status transitions:

  • INBOX → ATTACHED (via /attach with entryId — links to a specific 1:1 entry)
  • INBOX → CONVERTED (via /convert)
  • INBOX → ARCHIVED (via /archive)
  • No other transitions are allowed

Query parameters for the quick notes list endpoint:

  • page — Page number (default: 0)
  • size — Page size (default: 20)
  • status — Filter by status (INBOX, ATTACHED, CONVERTED, ARCHIVED)
  • personId — Filter by assigned person
  • selfAssigned — Filter by self-assigned flag (true/false)

Dashboard

Method Endpoint Description
GET /api/v1/dashboard Get dashboard data (overdue, due-soon, stale 1:1s, anniversaries)

Query parameters:

  • dueSoonDays — Number of days to look ahead for due-soon items (default: 3)
  • anniversaryLookaheadDays — Number of days to look ahead for anniversaries (default: 30)

Notifications

Method Endpoint Description
GET /api/v1/notifications List notifications (paginated)
GET /api/v1/notifications/unread-count Get unread notification count
POST /api/v1/notifications/{notificationId}/read Mark a notification as read
POST /api/v1/notifications/read-all Mark all notifications as read

Query parameters for the notifications list:

  • page — Page number (default: 0)
  • size — Page size (default: 20)
  • unreadOnly — Only return unread notifications (default: false)

Notification types:

  • ACTION_ITEM_OVERDUE — Action item past its due date
  • ACTION_ITEM_DUE_SOON — Action item due within the configured threshold
  • STALE_ONE_ON_ONE — 1:1 meeting overdue based on cadence
  • UPCOMING_ANNIVERSARY — Work anniversary approaching

Search

Method Endpoint Description
GET /api/v1/search Full-text search across all manager data

Query parameters:

  • q — Search query (required)
  • type — Filter by result type (repeatable): PERSON, ONE_ON_ONE_ENTRY, QUICK_NOTE, ACTION_ITEM, PDP_GOAL, PDP_UPDATE, KUDOS, STRATEGY_GOAL
  • page — Page number (default: 0)
  • size — Page size (default: 20, max: 100)

Notes:

  • All results are scoped by the authenticated user (security invariant)
  • Sensitive content snippets are hidden in search results (only title shown)
  • Uses PostgreSQL full-text search with GIN indexes, prefix matching, and relevance ranking
  • Encrypted sensitive fields are not searchable (trade-off for encryption at rest)
  • Strategy Goals follow the same rule: when a strategy goal is marked sensitive=true, its title and description are encrypted at rest and excluded from full-text search and GIN indexes. Queries against encrypted fields return no results by design.

Response fields:

  • results — Array of search results with id, type, title, snippet, personId, personName, sensitive, createdAt, relevanceScore
  • query — The original search query
  • totalCount — Total number of matching results
  • page / size / totalPages — Pagination metadata

Strategy Goals

Method Endpoint Description
POST /api/v1/strategy-goals Create a strategy goal
GET /api/v1/strategy-goals List strategy goals (paginated)
GET /api/v1/strategy-goals/{id} Get a strategy goal
PUT /api/v1/strategy-goals/{id} Update a strategy goal
DELETE /api/v1/strategy-goals/{id} Delete a strategy goal
POST /api/v1/strategy-goals/{id}/achieve Mark strategy goal as ACHIEVED
POST /api/v1/strategy-goals/{id}/drop Mark strategy goal as DROPPED
POST /api/v1/strategy-goals/{id}/links Link a PDP goal to strategy goal
GET /api/v1/strategy-goals/{id}/links Get linked PDP goals
DELETE /api/v1/strategy-goals/{id}/links/{pdpGoalId} Unlink a PDP goal
GET /api/v1/strategy-goals/{id}/alignment Get alignment score for goal
GET /api/v1/strategy-goals/alignment Get all alignment scores
GET /api/v1/strategy-goals/gap-analysis Get gap analysis
GET /api/v1/persons/{personId}/pdp-goals/{pdpGoalId}/strategy-goals Get strategy goals linked to a PDP goal
POST /api/v1/strategy-goals/ai-suggestions Generate AI link suggestions

Strategy Goal fields:

  • title — Required (max 500 chars)
  • description — Optional text (max 5000 chars)
  • targetDate — Optional date (ISO 8601 date)
  • status — ACTIVE, ACHIEVED, or DROPPED (default: ACTIVE)
  • sensitive — Optional boolean (default: false)

Encryption/Search trade-off:

  • When sensitive=true, title and description are stored encrypted (AES-256-GCM) and decrypted on read.
  • Encrypted fields cannot be indexed or searched. Sensitive strategy goals are excluded from search results, and queries against encrypted values return no matches by design.

Strategy Goal status transitions:

  • ACTIVE → ACHIEVED (via /achieve)
  • ACTIVE → DROPPED (via /drop)
  • No other transitions are allowed

Query parameters for strategy goals list:

  • page — Page number (default: 0)
  • size — Page size (default: 20)
  • status — Filter by status (ACTIVE, ACHIEVED, DROPPED)

Alignment Score fields:

  • strategyGoalId — Strategy goal UUID
  • strategyGoalTitle — Strategy goal title
  • totalActivePdpGoals — Total number of active PDP goals across all people
  • linkedPdpGoals — Number of PDP goals linked to this strategy goal
  • alignmentPercentage — Percentage of active PDP goals linked (0-100)

Gap Analysis fields:

  • unlinkedPdpGoals — Array of PDP goals not linked to any strategy goal
  • emptyStrategyGoals — Array of strategy goals with no linked PDP goals

User Settings

Method Endpoint Description
GET /api/v1/settings Get current user settings (returns defaults if none saved)
GET /api/v1/settings/ai-status Get resolved AI availability and config source (personal vs team defaults)
PUT /api/v1/settings Update user settings

Settings fields:

  • dueSoonDays — Days before due date to show "due soon" (1–30, default: 3)
  • staleOneOnOneDays — Days without a 1:1 before it's considered stale (1–90, default: 14)
  • anniversaryLookaheadDays — Days to look ahead for anniversaries (1–90, default: 30)
  • theme — UI theme: DARK or LIGHT (default: DARK)
  • showAchievements — Show achievement badges on dashboard (default: true)
  • notifyActionItemOverdue — Enable overdue action item notifications (default: true)
  • notifyActionItemDueSoon — Enable due-soon action item notifications (default: true)
  • notifyStaleOneOnOne — Enable stale 1:1 notifications (default: true)
  • notifyUpcomingAnniversary — Enable anniversary notifications (default: true)

Workspaces

Method Endpoint Description
POST /api/v1/workspaces Create a workspace
GET /api/v1/workspaces List workspaces
GET /api/v1/workspaces/{workspaceId} Get a workspace
PUT /api/v1/workspaces/{workspaceId} Update a workspace
DELETE /api/v1/workspaces/{workspaceId} Delete a workspace
PUT /api/v1/workspaces/persons/{personId}/workspace Assign a person to a workspace (or clear with empty body)

Triage Queue

Method Endpoint Description
GET /api/v1/triage Get the unified triage queue
POST /api/v1/triage/items/{itemId}/hint Get AI "next best action" hint for an item
POST /api/v1/triage/persons/{personId}/action-items/{actionItemId}/snooze Snooze an action item

Query parameters for the triage queue:

  • type — Filter by item type: ACTION_ITEM_OVERDUE, ACTION_ITEM_DUE_SOON, STALE_ONE_ON_ONE, UPCOMING_ANNIVERSARY
  • workspaceId — Filter by workspace (repeatable)
  • personId — Filter by person
  • scope — Owner scope: ALL (default) or MINE

Gamification

Method Endpoint Description
GET /api/v1/gamification/stats Get gamification stats (PDP completion %, 1:1 streak, badges, activity heatmap)

Query parameters:

  • heatmapDays — Number of days of activity history for the heatmap (default: 90)

Audit Log

Method Endpoint Description
GET /api/v1/audit-log List audit log entries (paginated)

Query parameters:

  • entityType — Filter by entity type (e.g., PERSON, ACTION_ITEM, PDP_GOAL, ...)
  • action — Filter by action (CREATE, UPDATE, DELETE, RESTORE, ...)
  • page — Page number (default: 0)
  • size — Page size (default: 20)

AI Endpoints

All AI endpoints require AI to be enabled and configured (personal config or team defaults) and respect Privacy Mode.

Method Endpoint Description
POST /api/v1/ai/refine-kudos Refine kudos draft using the SBI framework
POST /api/v1/ai/optimize-pdp-goal SMART-check and improve a PDP goal
POST /api/v1/ai/optimize-strategy-goal Optimize a strategy goal
POST /api/v1/ai/command Parse a natural-language command into typed actions
GET /api/v1/ai/command/directory Get person directory for command micro-context

Person-scoped AI endpoints (1:1 prep, performance narrative, trend radar, outcome extraction) are listed in the Person Directory table above.


Environment Variables

Backend (API)

Variable Description Required
DB_URL JDBC URL for PostgreSQL Yes
DB_USER Database username Yes
DB_PASSWORD Database password Yes
OIDC_ISSUER_URI OIDC issuer URI (authentik provider URL) Yes
OIDC_JWKS_URI JWKS endpoint URI Yes
ENCRYPTION_KEY Base64-encoded 32-byte AES key for sensitive field encryption (generate with: openssl rand -base64 32) No*
NOTIFICATION_CRON Cron expression for notification scheduler (default: 0 0 * * * * — every hour) No
NOTIFICATION_DUE_SOON_DAYS Days before due date to trigger "due soon" notifications (default: 3) No
NOTIFICATION_ANNIVERSARY_LOOKAHEAD_DAYS Days to look ahead for anniversary notifications (default: 7) No
METRICS_TOKEN Bearer token for securing the /actuator/prometheus metrics endpoint. If not set, the endpoint returns 403. Generate with: openssl rand -hex 32 No
AI_DEFAULT_BASE_URL Base URL of the OpenAI-compatible AI API for team-wide defaults (e.g., http://ollama:11434/v1) No
AI_DEFAULT_API_KEY API key for the team-wide AI provider (leave empty for local models like Ollama) No
AI_DEFAULT_MODEL Model name for team-wide AI defaults (e.g., llama3, gpt-4o) No

Frontend

Variable Description Required
NEXTAUTH_URL Canonical URL of the frontend Yes
NEXTAUTH_SECRET Auth.js session secret Yes
OIDC_CLIENT_ID OIDC client ID from authentik Yes
OIDC_CLIENT_SECRET OIDC client secret from authentik Yes
OIDC_ISSUER authentik OIDC issuer URL Yes
API_BASE_URL Internal URL to backend API (read at runtime, e.g., http://api:8080 for Docker or https://api.example.com for external) Yes

*ENCRYPTION_KEY is required when sensitive field encryption is enabled. Generate with: openssl rand -base64 32. Must be exactly 32 bytes when Base64-decoded (256-bit AES key). Without this key, sensitive content is stored in plaintext (the sensitive flag still works for UI labeling).

AI Team Defaults: Set AI_DEFAULT_BASE_URL and AI_DEFAULT_MODEL to provide AI features to all users without requiring individual configuration. Users who set their own AI server/model in Settings will use their personal config instead. This allows admins to share a team subscription (e.g., a shared Ollama instance) while letting power users override with their own provider.

See .env.example for a complete template with placeholder values.


Running Tests

# Backend — all tests (requires Docker for Testcontainers)
cd api && ./gradlew test

# Backend — specific layer
./gradlew test --tests "com.peoplemanager.domain.*"
./gradlew test --tests "com.peoplemanager.application.*"
./gradlew test --tests "com.peoplemanager.adapters.web.*"
./gradlew test --tests "com.peoplemanager.integration.*"

# Frontend — unit + component tests
cd frontend && npm test

# Frontend — tests with coverage
npm run test:coverage

# Frontend — end-to-end tests (requires running stack)
npm run test:e2e

# dev.sh tests
./tests/test_dev_sh.sh

All backend database tests use Testcontainers with real PostgreSQL — no H2.


Monitoring (Prometheus + Grafana)

CrewCaptain exposes a Prometheus-compatible metrics endpoint for monitoring.

Endpoints

Endpoint Auth Description
/actuator/health None Health check (used by Docker healthcheck)
/actuator/prometheus Bearer token (METRICS_TOKEN) Prometheus metrics scrape endpoint

Setup

  1. Set METRICS_TOKEN in your .env file:

    METRICS_TOKEN=$(openssl rand -hex 32)
  2. Configure your Prometheus scrape_configs:

    scrape_configs:
      - job_name: 'crewcaptain-api'
        metrics_path: '/actuator/prometheus'
        bearer_token: '<your-METRICS_TOKEN-value>'
        static_configs:
          - targets: ['api:8080']

Available Metrics

Metric Type Description
crewcaptain_one_on_ones Gauge Total number of 1:1 entries across all users
crewcaptain_one_on_ones_last_7_days Gauge 1:1 entries with meeting date in the last 7 days
jvm_memory_* Various JVM memory usage (heap, non-heap, buffers)
jvm_gc_* Various Garbage collection stats
hikaricp_* Various Database connection pool metrics
http_server_requests_* Timer HTTP request latency and count by endpoint
application_ready_time_seconds Gauge Application startup time

All metrics include the tag application="crewcaptain".

Security

  • If METRICS_TOKEN is not configured, the /actuator/prometheus endpoint returns 403 Forbidden.
  • All other actuator endpoints (except /actuator/health) are blocked.
  • The metrics endpoint does not expose any user data — only operational metrics.

Database Migrations

Schema changes are managed via Flyway. Current migrations:

Migration Description
V20250508120000 Create users table
V20250508120001 Create persons table
V20250508120002 Create pinned_remember_items table
V20250508120003 Create one_on_one_series table
V20250508120004 Create one_on_one_entries table
V20250508120005 Create agenda_items table
V20250510120000 Create action_items table
V20250510120001 Create pdp_goals table
V20250510120002 Create pdp_updates table
V20250510120003 Create kudos table
V20250510120004 Create quick_notes table
V20250510120005 Add attached_entry_id to quick_notes
V20250510120006 Create notifications table
V20250510120007 Add full-text search support (placeholder — no schema changes needed)
V20250510120008 Create user_settings table
V20250510120009 Add GIN indexes for full-text search (immutable wrapper functions + expression indexes)
V20250510120010 Add soft-delete support to persons table (deleted_at column + indexes)
V20250511120000 Create audit_log table with indexes
V20250511120001 Cascade person delete to child tables (action_items, pdp_goals, kudos, quick_notes)
V20250511120002 Create workspaces table
V20250515120000 Add self_assigned flag to quick_notes
V20250517120000 Add AI settings to user_settings
V20250517130000 Add AI writing style to user_settings
V20250518120000 Add custom AI prompts to user_settings
V20250518130000 Add outcome extractor prompt to user_settings
V20250519120000 Add trend radar prompt to user_settings
V20250524120000 Create strategy_goals table
V20250524120001 Create strategy_goal_pdp_goal_links table
V20250524120002 Add GIN index for strategy_goals full-text search
V20250525100000 Add link suggestions prompt to user_settings
V20250525120000 Add strategy optimization prompt to user_settings
V20250602120000 Add sticky-note fields to pinned_remember_items
V20250606120000 Add snoozed_until to action_items
V20250607120000 Add AI auto-execute flag and command terminal prompt to user_settings

New migrations must follow the naming convention: V{timestamp}__{description}.sql


authentik Setup (OIDC)

  1. In your authentik admin panel, create a new OAuth2/OIDC Provider:

    • Name: crewcaptain
    • Client type: Confidential
    • Redirect URIs: http://localhost:3000/api/auth/callback/oidc
    • Signing key: Select or create an RSA key
    • Scopes: Add the offline_access scope mapping (required for refresh tokens). If it doesn't exist, create a Property Mapping with scope name offline_access and expression return {}.
  2. Create an Application linked to the provider:

    • Name: CrewCaptain
    • Slug: crewcaptain
  3. Note the following values for your .env:

    • OIDC_CLIENT_ID — from the provider
    • OIDC_CLIENT_SECRET — from the provider
    • OIDC_ISSUER_URI / OIDC_ISSUERhttps://your-authentik/application/o/crewcaptain/
    • OIDC_JWKS_URIhttps://your-authentik/application/o/crewcaptain/jwks/

Note: Without the offline_access scope mapping, authentik will not issue refresh tokens and users will be forced to re-login when the access token expires (typically every 5 minutes).


Backup and Restore

Backup PostgreSQL

# Using Docker Compose
docker compose exec db pg_dump -U crewcaptain crewcaptain > backup_$(date +%Y%m%d).sql

# Direct connection
pg_dump -h localhost -U crewcaptain -d crewcaptain > backup_$(date +%Y%m%d).sql

Restore PostgreSQL

# Using Docker Compose
cat backup_20250508.sql | docker compose exec -T db psql -U crewcaptain -d crewcaptain

# Direct connection
psql -h localhost -U crewcaptain -d crewcaptain < backup_20250508.sql

Project Structure

/
├── dev.sh                     ← Local dev runner (./dev.sh backend | frontend)
├── docker-compose.yml         ← Production-like stack
├── .env.example               ← Environment variable template
│
├── api/                       ← Kotlin Spring Boot backend
│   ├── src/main/kotlin/com/peoplemanager/
│   │   ├── domain/            ← Aggregates, Value Objects, Enums (pure Kotlin, no framework deps)
│   │   │   └── service/       ← Domain services (CsvParser, formatters — pure logic)
│   │   ├── application/       ← Use Cases, Commands, Queries
│   │   │   ├── port/
│   │   │   │   ├── input/    ← Input Ports (CommandPort + QueryPort interfaces)
│   │   │   │   └── output/   ← Output Ports (Repositories, AI, Encryption)
│   │   │   ├── commands/      ← Command objects (write DTOs)
│   │   │   └── queries/       ← Query objects (read DTOs)
│   │   └── adapters/
│   │       ├── web/           ← REST Controllers + DTOs
│   │       ├── persistence/   ← JPA Repositories + Entities
│   │       ├── ai/            ← LLM/AI client adapter
│   │       ├── auth/          ← OIDC/JWT verification + user provisioning
│   │       ├── encryption/    ← AES-GCM encryption adapter
│   │       ├── metrics/       ← Prometheus custom metrics
│   │       └── scheduler/     ← Notification generation (hourly scheduled)
│   ├── src/main/resources/db/migration/ ← Flyway migrations
│   ├── src/test/kotlin/       ← Tests (domain, application, web, integration, architecture)
│   ├── ARCHITECTURE.md        ← Detailed architecture guide & migration playbook
│   ├── build.gradle.kts
│   └── Dockerfile
│
└── frontend/                  ← Next.js frontend
    ├── src/
    │   ├── app/               ← App Router pages (people list, detail, create)
    │   ├── components/        ← UI components (PersonCard, FilterBar, MoraleIndicator, etc.)
    │   ├── lib/               ← API client
    │   └── types/             ← TypeScript type definitions
    ├── __tests__/             ← Jest + React Testing Library (components, lib, pages)
    ├── e2e/                   ← Playwright end-to-end tests (planned)
    ├── package.json
    └── Dockerfile

CI/CD

The project ships pipelines for both hosting setups, so the same checks run wherever the code lives:

  • GitLab.gitlab-ci.yml (used by the maintainer's self-hosted GitLab instance).
  • GitHub.github/workflows/ci.yml (GitHub Actions).

Both run the identical logical stages.

Pipeline Stages

Stage Trigger Jobs
test All branches / PRs test-api (Gradle + Testcontainers), test-frontend (Jest)
build main only build-api (Docker image), build-frontend (Docker image)

Container Registry

Docker images are published on every push to main:

  • GitLab CI → the GitLab Container Registry (<registry>/api and <registry>/frontend).
  • GitHub Actions → the GitHub Container Registry (ghcr.io/<owner>/<repo>/api and .../frontend).

Both tag images with latest and the commit SHA.

Runner Requirements

  • GitLab: runners must support Docker-in-Docker (docker:24-dind service), used for both Testcontainers (backend tests) and image builds.
  • GitHub: the default ubuntu-latest runners ship with Docker preinstalled, so Testcontainers and image builds work with no extra services.

Privacy & Telemetry

CrewCaptain is privacy-first. Next.js telemetry is disabled in the Docker image via the NEXT_TELEMETRY_DISABLED=1 environment variable, set in both the build and runtime stages of the frontend Dockerfile. No anonymous usage data is sent to Vercel during builds or at runtime.

For local development outside Docker, you can disable telemetry manually:

npx next telemetry disable

Or set the environment variable in your shell:

export NEXT_TELEMETRY_DISABLED=1

See Next.js Telemetry for details on what would be collected if enabled.


Contributing

  1. Read AGENTS.md for the full development workflow and architecture rules
  2. Read api/ARCHITECTURE.md for hexagonal architecture guide and migration playbook
  3. Follow the mandatory workflow: Branch → Read → Plan → Test → Code → Verify → Docs → Log → Commit
  4. Every change must include tests — no exceptions
  5. All data queries must be scoped by userId (security invariant)
  6. Architecture boundaries are enforced by ArchUnit tests — the build will fail if violated
  7. Update README.md and PROGRESS.md with every change
  8. Use conventional commits (see AGENTS.md §4.3)

License

This project is licensed under the GNU Affero General Public License v3.0.

About

A self-hosted, privacy-first manager workspace for organizing people context, 1:1 history, development goals, action items, and kudos. CrewCaptain is not HR software — it's a private cockpit for people-centric leadership.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors