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):
- Get all groups the user belongs to (
user_groups) - For each group, get role assignments (
group_repo_roles) matching the target repo or global (NULL) - For each assigned role, check if it grants the required action (
role_permissions) - If any path grants access, return
allowed = true - Fallback: check legacy
user_repo_rolestable
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_ordercolumn) - 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}/previewreturns matching artifacts without deleting - Manual trigger:
POST /api/v1/repositories/{repo}/cleanupruns all enabled policies immediately - Scheduled: Background scheduler checks every
cleanup.schedule_intervalminutes (default: 60). Each repository'scleanup_interval_minutescontrols how often its policies run - Tracking: Each policy records
last_run_at,last_run_deleted(count), andlast_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):
- Environment variables — prefix
ORBITREPO_, dot-to-underscore mapping (e.g.,ORBITREPO_SERVER_PORT) - Config file —
orbitrepo.yamlsearched in.,./configs,/etc/orbitrepo - 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.portmust be 1-65535auth.jwt_secretmust not be emptystorage.typemust be"filesystem"or"s3"- If
storage.typeis"s3",storage.s3.bucketis 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