Skip to content

OrbitRepos - System Design Architecture

1. Introduction

OrbitRepos is a self-hosted, open-source artifact repository manager — comparable to JFrog Artifactory and Sonatype Nexus. It ships as a single Go binary with an embedded React SPA, requiring only PostgreSQL and a storage backend (local filesystem or S3-compatible) to operate.

Key characteristics:

  • Single binary deployment (Go + embedded React SPA)
  • 9 package formats: Docker, Maven, npm, PyPI, NuGet, Go modules, Helm, APT/Debian, Raw/Generic
  • 3 repository types: hosted, proxy (with caching), group (virtual aggregation)
  • Group-based RBAC with LDAP/OIDC integration
  • Cleanup policies with preview/dry-run
  • Prometheus metrics and structured audit logging
  • S3-compatible or filesystem storage

2. High-Level Architecture

graph TB
    subgraph Clients
        Browser["Browser / SPA"]
        Docker["docker CLI"]
        PackageCLIs["npm / pip / mvn / dotnet\ngo / helm / apt / curl"]
        CICD["CI/CD Pipelines"]
        Prometheus["Prometheus"]
    end

    subgraph OrbitRepo["OrbitRepo Server (Single Go Binary)"]
        direction TB
        Router["Chi Router\n+ Global Middleware"]
        SPA["Embedded React SPA\n(go:embed)"]
        API["REST API Layer\n(/api/v1/*)"]
        FormatHandlers["Format Handlers\nDocker | Maven | npm | PyPI\nNuGet | Go | Helm | APT | Raw"]
        AuthMW["Auth Middleware\nJWT / Basic / API Token"]
        AuthSvc["Auth Service\nLocal + LDAP + OIDC"]
        RepoSvc["Repository Service\nCRUD + Search + Browse"]
        CleanupSvc["Cleanup Service\nPolicies + Scheduler"]
        ProxyClient["Proxy Client\nRemote Fetch + Cache + Dedup"]
        GroupResolver["Group Resolver\nPriority-Ordered + Recursive"]
        Metrics["Prometheus Metrics\n12 Instruments"]
        StorageLayer["Storage Interface\nFilesystem | S3"]
    end

    subgraph External["External Services"]
        PostgreSQL["PostgreSQL 16\npgxpool + Auto-Migrations"]
        FS["Filesystem\n/data/storage"]
        S3["S3 / MinIO\nAWS SDK v2"]
        LDAP["LDAP Server"]
        OIDC["OIDC Provider\n(Keycloak, Auth0, etc.)"]
        RemoteRegs["Remote Registries\nDocker Hub, npmjs, PyPI, etc."]
    end

    Browser -->|"HTTP/HTTPS"| Router
    Docker -->|"Docker Registry V2 API"| Router
    PackageCLIs -->|"Format-specific protocols"| Router
    CICD -->|"REST API + Format APIs"| Router
    Prometheus -->|"GET /metrics"| Metrics

    Router --> SPA
    Router --> AuthMW
    AuthMW --> API
    AuthMW --> FormatHandlers
    API --> RepoSvc
    API --> AuthSvc
    API --> CleanupSvc
    FormatHandlers --> RepoSvc
    FormatHandlers --> ProxyClient
    FormatHandlers --> GroupResolver
    AuthSvc --> AuthMW
    ProxyClient --> RemoteRegs
    GroupResolver --> ProxyClient
    CleanupSvc --> RepoSvc

    RepoSvc --> PostgreSQL
    RepoSvc --> StorageLayer
    AuthSvc --> PostgreSQL
    AuthSvc --> LDAP
    AuthSvc --> OIDC
    CleanupSvc --> PostgreSQL
    CleanupSvc --> StorageLayer
    StorageLayer --> FS
    StorageLayer --> S3

    style OrbitRepo fill:#f8fafc,stroke:#0f172a,stroke-width:2px
    style External fill:#f1f5f9,stroke:#64748b
    style Clients fill:#fff,stroke:#64748b

3. Request Flow

3.1 Authenticated API Request

sequenceDiagram
    participant C as Client
    participant MW as Global Middleware
    participant Auth as Auth Middleware
    participant H as API / Format Handler
    participant Svc as Service Layer
    participant DB as PostgreSQL
    participant St as Storage

    C->>MW: HTTP Request
    Note over MW: RequestID → RealIP →<br/>Logger → Recoverer →<br/>Compress(5) → CORS →<br/>Metrics Instrumentation
    MW->>Auth: Authenticated Request

    alt Bearer Token
        Auth->>Auth: Try JWT (HS256, 24h expiry)
        Auth->>Auth: Fallback: Try API Token (orb_ prefix, SHA256)
    else Basic Auth
        Auth->>Auth: Check password prefix "orb_" → API Token
        Auth->>DB: AuthenticateLocal (bcrypt)
    end

    alt Auth Success
        Auth->>Auth: Set user in context
        Auth->>H: Forward request
    else Auth Failure + Anonymous Allowed
        Auth->>Auth: Create anonymous user context
        Auth->>H: Forward request
    else Auth Failure
        Auth-->>C: 401 Unauthorized
    end

    H->>Svc: Business logic call
    Svc->>DB: Query / Insert / Update
    Svc->>St: Read / Write artifact data
    Svc-->>H: Result
    H-->>C: JSON / Binary Response

3.2 Proxy Repository Request

sequenceDiagram
    participant C as Client
    participant FH as Format Handler
    participant PC as Proxy Client
    participant St as Storage
    participant DB as PostgreSQL
    participant RR as Remote Registry

    C->>FH: GET artifact (proxy repo)
    FH->>St: Check local cache
    alt Cache Hit (within TTL)
        St-->>FH: Return cached artifact
        FH-->>C: Serve from cache
    else Cache Miss
        FH->>PC: Fetch from remote
        Note over PC: Singleflight dedup<br/>(concurrent request coalescing)
        PC->>RR: HTTP GET (with auth if configured)
        Note over PC: Docker Hub: rewrite<br/>library/ prefix for<br/>official images
        RR-->>PC: Artifact data
        PC->>St: Cache artifact to storage
        PC->>DB: Upsert artifact record
        PC-->>FH: Return artifact
        FH-->>C: Serve artifact
    end

3.3 Group Repository Resolution

sequenceDiagram
    participant C as Client
    participant FH as Format Handler
    participant GR as Group Resolver
    participant M1 as Member 1 (Hosted)
    participant M2 as Member 2 (Proxy)
    participant M3 as Member 3 (Group)

    C->>FH: GET artifact (group repo)
    FH->>GR: Resolve artifact
    Note over GR: Iterate members in<br/>priority order (sort_order)

    GR->>M1: Try hosted member
    M1-->>GR: 404 Not Found

    GR->>M2: Try proxy member
    Note over M2: Checks local cache first,<br/>fetches remote if miss
    M2-->>GR: Found! Return artifact

    Note over GR: Stop iteration on<br/>first successful match<br/>(max depth: 5 for<br/>nested groups)

    GR-->>FH: Artifact from Member 2
    FH-->>C: Serve artifact

4. Component Architecture

4.1 Package Structure

cmd/orbitrepo/
  main.go                    # Entry point, graceful shutdown

internal/
  server/
    server.go                # HTTP server, chi router, all route definitions
    webui.go                 # Embedded SPA serving via go:embed

  api/
    handler.go               # REST API handlers (~1700 lines)
    middleware.go             # Auth middleware chain, RBAC checks

  config/
    config.go                # Viper-based YAML + env var config

  database/
    postgres.go              # pgxpool connection + golang-migrate
    migrations/              # Consolidated SQL migration (001_init)

  repository/
    model.go                 # Domain models and DTOs (~508 lines)
    service.go               # Repository & artifact business logic (~1422 lines)
    cleanup.go               # Cleanup policy engine (~618 lines)

  auth/
    local.go                 # Auth service: users, JWT, API tokens, RBAC (~1505 lines)
    ldap.go                  # LDAP bind+search, group sync
    oidc.go                  # OAuth2/OIDC authorization code flow

  storage/
    storage.go               # Storage interface definition
    filesystem.go            # Local filesystem backend
    s3.go                    # S3-compatible backend (AWS SDK v2)

  proxy/
    client.go                # Remote artifact fetch, caching, dedup
    group.go                 # Group repository member resolution

  format/
    docker/handler.go        # Docker Registry V2 API (1088 lines)
    maven/handler.go         # Maven repository (308 lines)
    npm/handler.go           # npm registry (430 lines)
    pypi/handler.go          # PyPI Simple + JSON API (455 lines)
    nuget/handler.go         # NuGet V3 feed (453 lines)
    gomod/handler.go         # Go module proxy (434 lines)
    raw/handler.go           # Raw/generic file storage (398 lines)
    helm/handler.go          # Helm chart repository (398 lines)
    apt/handler.go           # APT/Debian repository (610 lines)

  metrics/
    metrics.go               # Prometheus instrumentation (12 metrics)

  cleanup/
    scheduler.go             # Background cleanup scheduler

web/
  embed.go                   # go:embed directive for SPA dist/
  src/                       # React + TypeScript + Vite SPA

4.2 Dependency Injection Pattern

All 9 format handlers receive identical dependencies at construction:

handler := format.NewHandler(
    db.Pool,       // *pgxpool.Pool — direct database access
    store,         // storage.Storage — artifact read/write
    repoSvc,       // *repository.Service — business logic
    authSvc,       // *auth.Service — authentication & authorization
    proxyClient,   // *proxy.Client — remote registry fetch + caching
    groupResolver, // *proxy.GroupResolver — group member resolution
)

4.3 Storage Interface

The Storage interface abstracts over filesystem and S3 backends with 10 operations:

Method Signature Purpose
Put (ctx, path, reader, size) → error Write/overwrite an object
Get (ctx, path) → ReadCloser, error Retrieve object data
Delete (ctx, path) → error Remove an object
Exists (ctx, path) → bool, error Check existence
List (ctx, prefix) → []ObjectInfo, error List objects by prefix
Stat (ctx, path) → *ObjectInfo, error Get object metadata
GetRange (ctx, path, offset, length) → ReadCloser, error Byte-range read (for Docker layers, resume)
PutAppend (ctx, path, reader, offset) → int64, error Append for chunked uploads
Type () → string Backend type name ("filesystem" or "s3")
Usage (ctx, prefix) → int64, error Total bytes under prefix

5. Data Model

erDiagram
    users {
        uuid id PK
        varchar username UK
        varchar email
        varchar password_hash
        varchar display_name
        varchar auth_source "local | ldap | oidc"
        boolean is_admin
        boolean is_active
        timestamptz last_login_at
        timestamptz created_at
        timestamptz updated_at
    }

    api_tokens {
        uuid id PK
        uuid user_id FK
        varchar name
        varchar token_hash UK
        varchar token_prefix "first 8 chars"
        text_arr scopes
        timestamptz expires_at
        timestamptz last_used_at
        timestamptz created_at
    }

    repositories {
        uuid id PK
        varchar name UK
        repo_format format "docker|maven|npm|pypi|nuget|go|raw|helm|apt"
        repo_type type "hosted | proxy | group"
        text description
        boolean online
        varchar storage_path
        varchar remote_url "proxy only"
        varchar remote_username "proxy only"
        varchar remote_password "proxy only"
        int cache_ttl_minutes
        boolean allow_anonymous_read
        text tls_ca_cert "proxy only"
        boolean tls_skip_verify "proxy only"
        int cleanup_interval_minutes
        timestamptz created_at
        timestamptz updated_at
    }

    group_members {
        uuid id PK
        uuid group_repo_id FK
        uuid member_repo_id FK
        int sort_order
    }

    artifacts {
        uuid id PK
        uuid repository_id FK
        varchar name
        varchar version
        varchar path
        repo_format format
        varchar content_type
        bigint size
        varchar sha256
        varchar sha1
        varchar md5
        jsonb metadata "GIN indexed"
        varchar cached_from
        timestamptz cache_expires_at
        bigint download_count
        timestamptz last_downloaded_at
        uuid uploaded_by FK
        timestamptz created_at
        timestamptz updated_at
    }

    docker_manifests {
        uuid id PK
        uuid repository_id FK
        varchar image_name
        varchar digest "sha256:xxx"
        varchar media_type
        int schema_version
        bytea content
        bigint size
        timestamptz created_at
    }

    docker_tags {
        uuid id PK
        uuid repository_id FK
        varchar image_name
        varchar tag
        uuid manifest_id FK
        timestamptz created_at
        timestamptz updated_at
    }

    docker_blobs {
        uuid id PK
        uuid repository_id FK
        varchar digest "sha256:xxx"
        bigint size
        varchar content_type
        varchar storage_path
        timestamptz created_at
    }

    docker_uploads {
        uuid id PK
        uuid repository_id FK
        varchar uuid UK
        varchar image_name
        varchar state "uploading"
        bigint offset_bytes
        varchar storage_path
        timestamptz created_at
        timestamptz updated_at
    }

    roles {
        uuid id PK
        varchar name UK
        text description
        boolean is_system
        timestamptz created_at
        timestamptz updated_at
    }

    role_permissions {
        uuid role_id PK_FK
        permission_action action PK "read|write|delete|admin"
    }

    groups {
        uuid id PK
        varchar name UK
        text description
        boolean is_system
        varchar source "local | ldap"
        timestamptz created_at
        timestamptz updated_at
    }

    user_groups {
        uuid id PK
        uuid user_id FK
        uuid group_id FK
        timestamptz created_at
    }

    group_repo_roles {
        uuid id PK
        uuid group_id FK
        uuid repository_id FK "NULL = global"
        uuid role_id FK
        timestamptz created_at
    }

    cleanup_policies {
        uuid id PK
        uuid repository_id FK
        varchar name
        text description
        boolean enabled
        int max_age_days
        varchar age_basis "created | last_downloaded"
        int max_versions
        varchar regex_include
        varchar regex_exclude
        boolean prerelease_only
        timestamptz last_run_at
        int last_run_deleted
        bigint last_run_freed_bytes
        timestamptz created_at
        timestamptz updated_at
    }

    audit_log {
        uuid id PK
        uuid user_id FK
        varchar username
        varchar action
        varchar resource_type
        varchar resource_id
        jsonb details
        inet ip_address
        text user_agent
        timestamptz created_at
    }

    system_settings {
        varchar key PK
        text value
        timestamptz updated_at
    }

    permissions {
        uuid id PK
        uuid user_id FK
        uuid repository_id FK "NULL = global"
        permission_action action
        timestamptz created_at
    }

    user_repo_roles {
        uuid id PK
        uuid user_id FK
        uuid repository_id FK "NULL = global"
        uuid role_id FK
        timestamptz created_at
    }

    users ||--o{ api_tokens : "has"
    users ||--o{ user_groups : "belongs to"
    users ||--o{ audit_log : "generates"
    users ||--o{ permissions : "has (legacy)"
    users ||--o{ user_repo_roles : "assigned (legacy)"
    users ||--o{ artifacts : "uploads"

    groups ||--o{ user_groups : "contains"
    groups ||--o{ group_repo_roles : "assigned"

    roles ||--o{ role_permissions : "grants"
    roles ||--o{ group_repo_roles : "used in"
    roles ||--o{ user_repo_roles : "used in (legacy)"

    repositories ||--o{ group_members : "is group parent"
    repositories ||--o{ group_members : "is group member"
    repositories ||--o{ artifacts : "contains"
    repositories ||--o{ docker_manifests : "contains"
    repositories ||--o{ docker_tags : "contains"
    repositories ||--o{ docker_blobs : "contains"
    repositories ||--o{ docker_uploads : "has active"
    repositories ||--o{ cleanup_policies : "has"
    repositories ||--o{ group_repo_roles : "scoped to"
    repositories ||--o{ permissions : "scoped to (legacy)"
    repositories ||--o{ user_repo_roles : "scoped to (legacy)"

    docker_manifests ||--o{ docker_tags : "referenced by"

5.1 Key Database Features

Feature Implementation
UUIDs uuid_generate_v4() via uuid-ossp extension
Full-text search tsvector + GIN index on artifacts(name, version, path)
JSONB metadata GIN-indexed metadata column on artifacts for format-specific data
Auto-updated timestamps update_updated_at_column() trigger on 8 tables
Migrations Single consolidated SQL file (001_init) applied via golang-migrate (embedded in binary)
Enum types repo_type, repo_format, permission_action — all values defined in initial schema
Trigram support pg_trgm extension enabled for fuzzy text matching

6. Authentication & Authorization

6.1 Authentication Chain

The auth middleware (internal/api/middleware.go) tries credentials in this order:

1. Bearer token header
   ├── Try as JWT (HS256, 24h default expiry)
   └── Try as API Token (orb_ prefix, SHA256 hashed lookup)

2. Basic Auth header
   ├── If password starts with "orb_" → validate as API Token
   └── Otherwise → authenticate against local DB (bcrypt)
   └── If local fails → try LDAP (if enabled)

3. No credentials
   ├── If global anonymous access enabled → create anonymous context
   ├── If per-repo anonymous read enabled (format routes only) → allow reads
   └── Otherwise → 401 Unauthorized

6.2 RBAC Model

Users ──► User Groups ──► Groups ──► Group Repo Roles ──► Roles ──► Role Permissions
                                    Repository (or NULL for global)

System Roles (seeded in initial migration):

Role Permissions Description
reader read Read-only access
deployer read, write Deploy artifacts
maintainer read, write, delete Manage artifacts
repo-admin read, write, delete, admin Full repository control

System Groups (seeded in initial migration):

Group Default Role Scope
admins repo-admin Global (all repos)
users reader Global (all repos)

Permission Resolution (auth.Service.CheckPermission):

  1. Get all groups the user belongs to (user_groups)
  2. For each group, get role assignments (group_repo_roles) matching the target repo or global (NULL)
  3. For each assigned role, check if it grants the required action (role_permissions)
  4. If any path grants access, return allowed = true
  5. Fallback: check legacy user_repo_roles table

6.3 External Identity Providers

Provider Protocol Features
LDAP LDAP bind+search Configurable via UI, group sync to local groups, custom user/email attributes
OIDC OAuth2 Authorization Code Configurable via UI, auto user creation on first login, ID token parsing

7. Repository Types

7.1 Hosted Repository

  • Accepts direct artifact uploads
  • Stores artifacts in the configured storage backend
  • Full CRUD operations
  • Path: /{format}/{repoName}/...

7.2 Proxy Repository

  • Caches artifacts from a configured remote URL
  • Configurable cache TTL (default: 1440 minutes / 24h)
  • Singleflight pattern prevents duplicate concurrent fetches for the same artifact
  • Docker Hub special handling: rewrites library/ prefix for official images
  • Custom TLS support: CA certificate PEM and skip-verify flag
  • Remote authentication: username/password for private registries

7.3 Group Repository

  • Virtual repository aggregating multiple member repositories
  • Members iterated in priority order (sort_order column)
  • First successful match wins (short-circuit)
  • Supports nested groups (recursive resolution, max depth: 5)
  • Read-only — cannot upload to a group repository

8. Format Handlers

Format Route Prefix Protocol Key Capabilities
Docker /v2/ Docker Registry V2 Manifest CRUD, tag listing, blob GET/HEAD, chunked blob uploads (start/patch/complete/cancel/status), catalog, multi-arch support
Maven /maven/{repo}/ Path-based GET/PUT Maven coordinate parsing (groupId:artifactId:version), SHA256/SHA1/MD5 checksums
npm /npm/{repo}/ npm Registry API Package metadata, scoped packages (@scope/pkg), _attachments base64 publish, tarball download, /-/v1/search
PyPI /pypi/{repo}/ PEP 503 Simple + PEP 691 JSON Simple index, package page with links, twine-compatible multipart upload, name normalization
NuGet /nuget/{repo}/ NuGet V3 Service index, search, flat container (versions/download), registration index/leaf, multipart publish
Go /go/{repo}/ GOPROXY (/@v/) list, latest, .info, .mod, .zip, module path encoding
Raw /raw/{repo}/ REST (GET/PUT/DELETE) Arbitrary file paths, directory listing as JSON, MIME detection
Helm /helm/{repo}/ ChartMuseum-compatible index.yaml generation, chart upload (Chart.yaml extraction from .tgz), chart download
APT /apt/{repo}/ APT repository Release/InRelease generation (MD5+SHA256), Packages/Packages.gz index, pool download, .deb upload

All handlers support hosted, proxy, and group repository types with identical fallback patterns.


9. Cleanup System

9.1 Policy Engine

Multiple cleanup policies can be defined per repository. Policies are evaluated with OR logic between them — if any policy matches an artifact, it is eligible for deletion. Within a single policy, all non-null criteria are ANDed together.

Criterion Description
max_age_days Delete artifacts older than N days
age_basis Calculate age from created timestamp or last_downloaded timestamp
max_versions Keep only the latest N versions per artifact name (sorted by creation date, newest kept)
regex_include Only target artifacts whose name/path matches this regex
regex_exclude Skip artifacts whose name/path matches this regex
prerelease_only Only target pre-release/snapshot versions (detected via semver heuristics: -alpha, -beta, -rc, -SNAPSHOT, etc.)

9.2 Execution

  • Preview/Dry-run: GET /api/v1/repositories/{repo}/cleanup-policies/{id}/preview returns matching artifacts without deleting
  • Manual trigger: POST /api/v1/repositories/{repo}/cleanup runs all enabled policies immediately
  • Scheduled: Background scheduler checks every cleanup.schedule_interval minutes (default: 60). Each repository's cleanup_interval_minutes controls how often its policies run
  • Tracking: Each policy records last_run_at, last_run_deleted (count), and last_run_freed_bytes

10. Observability

10.1 Prometheus Metrics

All metrics use the orbitrepo_ namespace. Exposed at GET /metrics (no auth required).

Metric Type Labels Description
http_requests_total Counter method, path, status Total HTTP requests
http_request_duration_seconds Histogram method, path Request latency (default buckets)
artifact_uploads_total Counter format Upload operations
artifact_downloads_total Counter format Download operations
artifact_upload_bytes_total Counter format Bytes uploaded
artifact_download_bytes_total Counter format Bytes downloaded
storage_usage_bytes Gauge Total storage consumption
repositories_total Gauge format, type Repository count by format and type
artifacts_total Gauge Total artifact count
proxy_requests_total Counter format, result Proxy cache hit/miss/error
auth_attempts_total Counter method, result Auth attempts (local/ldap/oidc/token, success/failure)
active_connections Gauge Current active HTTP connections

Path labels are normalized to prevent high cardinality (e.g., /v2/*, /maven/*, /api/v1/repositories).

10.2 Structured Logging

Uses Go's standard log/slog with JSON or text format (configurable). Every HTTP request is logged with: method, path, status, bytes, duration_ms, remote address, request_id.

10.3 Audit Log

Stored in PostgreSQL audit_log table. Records: user, action, resource type, resource ID, details (JSONB), IP address, user agent. Filterable by user, action, resource type, and date range via the API and UI.


11. Deployment Architecture

graph LR
    subgraph Docker Compose
        direction TB

        subgraph Build["3-Stage Docker Build"]
            S1["Stage 1: node:20-alpine\nBuild React SPA\nnpm ci → npm run build"]
            S2["Stage 2: golang:1.22-alpine\nBuild Go Binary\nCGO_ENABLED=0, ldflags"]
            S3["Stage 3: alpine:3.19\nMinimal Runtime\nnon-root user (1000:1000)"]
            S1 --> S2 --> S3
        end

        subgraph Services["Container Services"]
            App["orbitrepo\nPort: 8080\nHealthcheck: GET /health\nVolume: orbitrepo-data"]
            PG["postgres:16-alpine\nPort: 5432\nHealthcheck: pg_isready\nVolume: postgres-data"]
            MinIO["minio (optional, 's3' profile)\nPort: 9000 (API), 9001 (Console)\nHealthcheck: mc ready\nVolume: minio-data"]
            Init["minio-init (sidecar)\nCreates default bucket\nRuns once on startup"]
        end

        S3 -.-> App
        App -->|"pgxpool"| PG
        App -->|"AWS SDK v2\n(path-style)"| MinIO
        Init -->|"mc mb"| MinIO
    end

    subgraph Config["Configuration"]
        YAML["orbitrepo.yaml\nViper config file"]
        ENV["Environment Variables\nPrefix: ORBITREPO_\nOverrides YAML"]
        CLI["Config Search Paths\n. | ./configs | /etc/orbitrepo"]
    end

    Config -.-> App

    style Build fill:#e2e8f0,stroke:#64748b
    style Services fill:#f1f5f9,stroke:#0f172a
    style Config fill:#fff,stroke:#64748b

11.1 Runtime Environment

Property Value
Base image alpine:3.19
User orbitrepo (UID/GID 1000) — non-root
Data directory /data/storage (configurable)
Config directory /data/config
Default port 8080
Healthcheck wget -qO- http://localhost:8080/health every 30s
Max request body 10 GB (configurable)
Read timeout 30s
Write timeout 300s (5 min — supports large artifact uploads)
Graceful shutdown 30s

11.2 Database

Property Value
Engine PostgreSQL 16
Driver pgx v5 via pgxpool
Connection pool max_open: 25, max_idle: 5, lifetime: 30m, idle_time: 5m
Migrations Single consolidated SQL file (001_init), auto-applied on startup via golang-migrate
Extensions uuid-ossp (UUIDs), pg_trgm (trigram fuzzy matching)

12. Configuration

Configuration is loaded via Viper with the following precedence (highest first):

  1. Environment variables — prefix ORBITREPO_, dot-to-underscore mapping (e.g., ORBITREPO_SERVER_PORT)
  2. Config fileorbitrepo.yaml searched in ., ./configs, /etc/orbitrepo
  3. Defaults — hardcoded in config.go

12.1 Configuration Structure

server:
  host: "0.0.0.0"              # Listen address
  port: 8080                    # Listen port
  read_timeout: 30              # Seconds
  write_timeout: 300            # Seconds (5 min for large uploads)
  max_request_body: 10737418240 # 10 GB
  graceful_timeout: 30          # Seconds

database:
  host: "localhost"
  port: 5432
  user: "orbitrepo"
  password: "orbitrepo"
  name: "orbitrepo"
  ssl_mode: "disable"
  max_open_conns: 25
  max_idle_conns: 5
  max_conn_lifetime: "30m"
  max_conn_idle_time: "5m"
  health_check_period: "30s"

storage:
  type: "filesystem"            # "filesystem" or "s3"
  base_dir: "/var/lib/orbitrepo/data"
  s3:
    endpoint: ""
    region: "us-east-1"
    bucket: ""
    access_key_id: ""
    secret_access_key: ""
    use_path_style: true        # Required for MinIO
    disable_ssl: false

auth:
  jwt_secret: "change-me-in-production"
  jwt_expiry: 24                # Hours
  admin_username: "admin"
  admin_password: "admin123"
  allow_anonymous: false
  ldap:
    enabled: false
    host: ""
    port: 389
    use_ssl: false
    bind_dn: ""
    bind_password: ""
    base_dn: ""
    user_filter: "(uid=%s)"
    group_filter: ""
    email_attr: "mail"
    username_attr: "uid"
  oidc:
    enabled: false
    issuer_url: ""
    client_id: ""
    client_secret: ""
    redirect_url: ""
    scopes: ["openid", "profile", "email"]

log:
  level: "info"                 # debug, info, warn, error
  format: "json"                # json, text

proxy:
  connect_timeout: 10           # Seconds
  read_timeout: 30              # Seconds
  cache_ttl: 1440               # Minutes (24 hours)

cleanup:
  enabled: true
  schedule_interval: 60         # Minutes

12.2 Validation Rules

  • server.port must be 1-65535
  • auth.jwt_secret must not be empty
  • storage.type must be "filesystem" or "s3"
  • If storage.type is "s3", storage.s3.bucket is required

13. API Route Map

13.1 Public Endpoints (No Auth)

Method Path Handler
GET /health Health check
GET /api/v1/status System status
GET /metrics Prometheus metrics
POST /auth/login Login (username/password → JWT)
POST /auth/token Login alias
GET /auth/oidc/login OIDC redirect
GET /auth/oidc/callback OIDC callback
GET /auth/oidc/status OIDC enabled status

13.2 Management API (JWT/Token Auth Required)

Method Path Access Description
GET /api/v1/repositories All List repositories
POST /api/v1/repositories Admin Create repository
GET /api/v1/repositories/{name} All Get repository
PUT /api/v1/repositories/{name} Admin Update repository
DELETE /api/v1/repositories/{name} Admin Delete repository
GET /api/v1/repositories/{name}/stats All Repository statistics
POST /api/v1/repositories/{name}/cleanup Admin Trigger cleanup
GET/POST/PUT/DELETE /api/v1/repositories/{name}/cleanup-policies/... Admin (write) Cleanup policy CRUD
GET /api/v1/artifacts All List artifacts
GET /api/v1/artifacts/search All Full-text search
GET/POST/DELETE /api/v1/users/... Admin User management
GET /api/v1/users/me All Current user info
GET/POST/DELETE /api/v1/tokens/... All API token management
GET/POST/DELETE /api/v1/roles/... Admin Role management
GET/POST/DELETE /api/v1/groups/... Admin Group management
GET/POST/DELETE /api/v1/group-role-assignments/... Admin Group role assignments
GET/PUT/POST /api/v1/settings/auth/... Admin LDAP/OIDC settings
GET /api/v1/audit Admin Audit log
GET /api/v1/dashboard All Dashboard stats
GET /api/v1/browse/... All Browse repositories
GET /api/v1/system/info All System info

13.3 Format API Routes

Format Base Route Auth
Docker /v2/... OptionalAuth (Docker auth flow)
Maven /maven/{repoName}/... FormatRouteAuth (RBAC)
npm /npm/{repoName}/... FormatRouteAuth (RBAC)
PyPI /pypi/{repoName}/... FormatRouteAuth (RBAC)
NuGet /nuget/{repoName}/... FormatRouteAuth (RBAC)
Go /go/{repoName}/... FormatRouteAuth (RBAC)
Raw /raw/{repoName}/... FormatRouteAuth (RBAC)
Helm /helm/{repoName}/... FormatRouteAuth (RBAC)
APT /apt/{repoName}/... FormatRouteAuth (RBAC)

13.4 Global Middleware Stack (Order)

1. chimw.RequestID        — Assign unique request ID
2. chimw.RealIP           — Extract real client IP
3. requestLogger          — Structured HTTP request logging (slog)
4. chimw.Recoverer        — Panic recovery
5. chimw.Compress(5)      — gzip compression (level 5)
6. cors.Handler           — CORS (all origins, credentials allowed)
7. metrics.InstrumentHandler — Prometheus request instrumentation