Files
headquarter/apps/api
Alex Blank ea006b68c2 fix: mount config profile files individually instead of replacing directories
The previous sorting fix exposed a deeper bug: ResolvedMount always
mounted its staging directory as a single bind mount. When a config
profile mount targeted /workspace/x/y and contained a single file
z.json, the staging directory (containing only z.json) replaced the
ENTIRE /workspace/x/y directory, hiding all sibling files from git
repo mounts.

- Change apply_resolved_profile to mount each file individually:
  - source: staging_dir/relative_path
  - target: expanded_target/relative_path
  - Sibling files from other mounts are preserved.
  - Empty mounts produce no volume entries.

- Keep volume sorting (parent paths before child paths) which is
  still necessary for directory mounts and ensures parent dirs exist
  before file mounts inside them.

- Add 4 unit tests for file-level mount behavior.

Quality gates: pytest (218 passed, 6 pre-existing), tsc --noEmit (clean)
2026-05-29 11:58:50 +02:00
..

Headquarter API

The backend API for Headquarter - a self-hosted platform for managing projects, git repositories, and development tools.

Overview

Built with FastAPI and SQLAlchemy (async), using PostgreSQL for data storage and Docker for tool instance management.

Tech Stack

  • Framework: FastAPI (Python 3.12+)
  • Database: PostgreSQL 15+ with asyncpg
  • ORM: SQLAlchemy 2.0 (async)
  • Auth: OAuth2 via Authentik with session cookies
  • Migrations: Alembic
  • Tools: Docker Compose for instance management

Quick Start

Prerequisites

  • Python 3.12+
  • PostgreSQL 15+ running locally
  • Docker (for tool instances)

Setup

cd apps/api

# Create virtual environment
python -m venv .venv
source .venv/bin/activate

# Install dependencies
pip install -e ".[dev]"

# Set up database
# Ensure PostgreSQL is running with a 'headquarter' database

# Run migrations
alembic upgrade head

# Start development server
uvicorn src.main:app --reload --port 8000

The API will be available at http://localhost:8000.

Interactive Documentation

Once running, visit:

Environment Variables

Variable Required Default Description
DATABASE_URL Yes - PostgreSQL connection string
API_BASE_URL Yes - Public API URL (e.g., https://api.example.com)
AUTHENTIK_DOMAIN Yes - Authentik server domain
AUTHENTIK_CLIENT_ID Yes - OAuth2 client ID
AUTHENTIK_CLIENT_SECRET Yes - OAuth2 client secret
AUTHENTIK_APPLICATION_SLUG Yes - Authentik application slug
WEB_BASE_URL Yes - Public frontend URL
SESSION_SECRET Yes - Secret for session cookie signing
COOKIE_DOMAIN No - Cookie domain (e.g., .example.com)
UPLOAD_DIR No ./uploads Directory for file uploads
REPO_BASE_PATH No ./repositories Base path for git repositories
INSTANCES_BASE_PATH No ./instances Base path for tool instances
LOG_LEVEL No INFO Logging level

Development

Running Tests

# Run all tests
pytest

# Run specific test category
pytest -m unit          # Unit tests (no DB)
pytest -m integration   # Integration tests (requires DB)

# Run with coverage
pytest --cov=src --cov-report=html

Code Quality

# Format code
ruff format src tests

# Lint
ruff check src tests

# Type check
mypy src

Database Migrations

# Create new migration
alembic revision --autogenerate -m "description"

# Apply migrations
alembic upgrade head

# Rollback one migration
alembic downgrade -1

# Show current revision
alembic current

Architecture

Directory Structure

src/
├── api/              # API endpoint routers
│   ├── auth.py       # OAuth2 authentication
│   ├── dashboard.py  # Dashboard summary
│   ├── git_repositories.py  # Git repo management
│   ├── health.py     # Health checks
│   ├── projects.py   # Project CRUD
│   ├── ssh_keys.py   # SSH key management
│   ├── terminal.py   # WebSocket terminal
│   ├── tool_instances.py  # Tool instance management
│   ├── tool_types.py # Tool type definitions
│   ├── user_config.py # User preferences
│   └── users.py      # User profile
├── auth/             # Authentication logic
│   ├── cookies.py    # Cookie utilities
│   ├── dependencies.py # Auth dependencies
│   ├── oidc.py       # OpenID Connect
│   └── session.py    # Session management
├── config.py         # Application settings
├── database.py       # Database setup
├── main.py           # FastAPI application
├── models/           # SQLAlchemy models
├── schemas/          # Pydantic schemas
├── services/         # Business logic
│   ├── docker.py     # Docker Compose management
│   ├── terminal_manager.py  # Terminal sessions
│   └── terminal_session.py  # Terminal I/O
└── utils/            # Utilities
    ├── git_control.py      # Git operations
    ├── git_files.py        # File operations
    ├── git_history.py      # History extraction
    └── git_url_parser.py   # URL parsing

Authentication Flow

  1. User clicks "Login" → redirects to Authentik OAuth
  2. Authentik redirects back with authorization code
  3. API exchanges code for tokens and fetches user info
  4. API creates session cookie (HMAC-signed, httpOnly)
  5. Frontend stores nothing - cookie sent automatically
  6. Subsequent requests include cookie for authentication

Data Flow

Client → FastAPI Router → Auth Dependency → Service Layer → Database
                ↓
         Pydantic Models (validation)
                ↓
         SQLAlchemy Models (ORM)
                ↓
         PostgreSQL (storage)

API Endpoints

Authentication

  • GET /auth/login - Initiate OAuth login
  • GET /auth/callback - OAuth callback
  • GET /auth/me - Get current user
  • POST /auth/logout - Logout

Projects

  • GET /projects - List projects
  • POST /projects - Create project
  • GET /projects/{id} - Get project
  • PUT /projects/{id} - Update project
  • DELETE /projects/{id} - Delete project

Git Repositories

  • GET /projects/{id}/repositories - List repositories
  • POST /projects/{id}/repositories - Create repository
  • GET /projects/{id}/repositories/{id} - Get repository
  • DELETE /projects/{id}/repositories/{id} - Delete repository
  • GET /projects/{id}/repositories/{id}/files - List files
  • GET /projects/{id}/repositories/{id}/files/content - Get file content
  • POST /projects/{id}/repositories/{id}/files/content - Update file
  • GET /projects/{id}/repositories/{id}/branches - List branches
  • GET /projects/{id}/repositories/{id}/history - Commit history
  • GET /projects/{id}/repositories/{id}/commits/{hash} - Commit detail

Tool Types

  • GET /tool-types - List tool types
  • POST /tool-types - Create tool type
  • GET /tool-types/{id} - Get tool type
  • PUT /tool-types/{id} - Update tool type
  • DELETE /tool-types/{id} - Delete tool type

Tool Instances

  • GET /tool-instances - List instances
  • POST /tool-instances - Create instance
  • GET /tool-instances/{id} - Get instance
  • POST /tool-instances/{id}/start - Start instance
  • POST /tool-instances/{id}/stop - Stop instance
  • POST /tool-instances/{id}/restart - Restart instance
  • DELETE /tool-instances/{id} - Delete instance
  • GET /tool-instances/{id}/logs - Get logs

Terminal

  • WS /ws/tool-instances/{id}/terminal - WebSocket terminal

Users

  • GET /users/me - Get profile
  • PUT /users/me - Update profile
  • POST /users/me/avatar - Upload avatar
  • GET /users/me/config - Get config
  • PATCH /users/me/config - Update config

SSH Keys

  • GET /ssh-keys - List keys
  • POST /ssh-keys - Create key
  • DELETE /ssh-keys/{id} - Delete key

Health

  • GET /health - System health
  • GET /health/db - Database health

Deployment

See the deployment documentation for Docker and Traefik setup.

Contributing

  1. Follow PEP 8 style guide
  2. Add tests for new endpoints
  3. Update documentation
  4. Run quality gates before committing