inapt
A minimal Debian/Ubuntu APT repository proxy written in Rust. It exposes a valid APT repository structure over HTTP while sourcing .deb packages directly from GitHub Release assets. Useful for distributing packages without hosting your own artifact storage.
Features
-
Full APT Repository Structure: Exposes standard Debian repository endpoints:
dists/<suite>/Release- Repository metadatadists/<suite>/InRelease- Signed repository metadatadists/<suite>/Release.gpg- Detached GPG signaturedists/<suite>/<component>/binary-<arch>/Packages(.gz)- Package indicesdists/<suite>/<component>/binary-<arch>/by-hash/SHA256/<hash>- By-hash index retrievaldists/<suite>/<component>/i18n/Translation-<lang>(.gz|.bz2|.xz)- Package descriptionspool/main/.../*.deb- Package downloads (302 redirects to GitHub)
-
GPG Signing: Full support for signed releases with
InReleaseandRelease.gpg -
By-Hash Support: Implements
Acquire-By-Hashfor atomic index updates -
Translation Files: Serves
Translation-enfiles with package descriptions in multiple compression formats (gzip, bzip2, xz) -
GitHub Integration:
- Dynamically builds Packages and Release metadata from GitHub Release assets
- 302 redirects
.debdownloads to GitHub (leverages GitHub CDN) - Supports multiple repositories
- Optional authentication for private repositories and higher rate limits
-
Persistence: SQLite backend for tracking scanned releases and caching package metadata
-
Incremental Synchronization: Only processes new releases, skipping already-scanned ones
-
Observability: Full OpenTelemetry support for tracing, metrics, and logs
Quick Start
1. Generate GPG keys
Extract the public key to share with users:
# The public key is printed first in the output
2. Configure
Create a config.toml file:
[]
= "My Repository"
= "Debian"
= "stable"
= "1.0.0"
= "cucumber"
= "Packages from GitHub releases"
= ["myorg/myproject", "myorg/another-project"]
[]
# Optional: for private repos or higher rate limits
# token = "ghp_..."
[]
= "0.0.0.0"
= 3000
[]
= "resources/private-key.pem"
# passphrase = "optional-passphrase"
[]
= "inapt.db"
[]
= 3600 # Sync every hour (default: 43200 = 12 hours)
3. Run
# Or with custom config path:
CONFIG_PATH=/path/to/config.toml
The proxy listens on 0.0.0.0:3000 by default.
4. Configure APT client
Add the repository and GPG key:
# Add the GPG key
|
# Add the repository
| \
# Update and install
For testing without signature verification (not recommended for production):
| \
Configuration Reference
Configuration File (TOML)
| Section | Key | Description | Default |
|---|---|---|---|
core |
origin |
Repository origin field | |
core |
label |
Repository label | |
core |
suite |
Distribution suite (e.g., stable) | |
core |
version |
Repository version | |
core |
codename |
Distribution codename | |
core |
description |
Repository description | |
core |
repositories |
List of GitHub repos (owner/repo) | |
github |
base_url |
GitHub API URL | https://api.github.com |
github |
token |
GitHub token (optional) | |
github |
max_retry |
Max HTTP retries | 5 |
github |
timeout |
Request timeout (seconds) | 60 |
http_server |
address |
Bind address | 0.0.0.0 |
http_server |
port |
Bind port | 3000 |
pgp_cipher |
private_key_path |
Path to GPG private key | |
pgp_cipher |
passphrase |
Key passphrase (optional) | |
sqlite |
path |
SQLite database path | |
worker |
interval |
Sync interval (seconds) | 43200 (12h) |
Environment Variables (Tracing)
| Variable | Description | Default |
|---|---|---|
TRACING_MODE |
console or otel |
console |
TRACING_LEVEL |
Log level filter | info |
TRACING_CONSOLE_COLOR |
Enable colored output | true |
TRACING_OTEL_ENDPOINT |
OTLP gRPC endpoint | http://localhost:4317 |
TRACING_OTEL_INTERNAL_LEVEL |
Log level for OpenTelemetry internals | error |
ENV |
Deployment environment name | local |
HOST_NAME |
Override system hostname (useful in containers) | System hostname |
CONTAINER_ID |
Container ID for container environments |
OpenTelemetry Resource Attributes
When TRACING_MODE=otel, the following OpenTelemetry semantic convention resource attributes are automatically set:
| Attribute | Source |
|---|---|
service.name |
Package name (inapt) |
service.version |
Package version |
deployment.environment.name |
ENV environment variable |
telemetry.sdk.name |
opentelemetry |
telemetry.sdk.language |
rust |
telemetry.sdk.version |
SDK version |
process.pid |
Current process ID |
process.executable.path |
Full path to executable |
process.executable.name |
Executable filename |
process.command_args |
Command line arguments |
os.type |
Operating system type |
host.name |
HOST_NAME env var or system hostname |
host.arch |
CPU architecture |
container.id |
CONTAINER_ID env var (if set) |
Example: Console Tracing (Development)
TRACING_MODE=console TRACING_LEVEL=debug
Example: OpenTelemetry with Jaeger
# Start Jaeger
# Run inapt with OTLP export
TRACING_MODE=otel \
TRACING_OTEL_ENDPOINT=http://localhost:4317 \
ENV=development \
# View traces at http://localhost:16686
Example: Docker/Kubernetes with Container Attributes
# docker-compose.yml
services:
inapt:
image: inapt:latest
environment:
- TRACING_MODE=otel
- TRACING_OTEL_ENDPOINT=http://otel-collector:4317
- ENV=production
- CONTAINER_ID=${HOSTNAME} # Docker sets HOSTNAME to container ID
- HOST_NAME=apt.example.com # Real hostname for identification
# Kubernetes deployment
spec:
containers:
- name: inapt
env:
- name: TRACING_MODE
value: "otel"
- name: TRACING_OTEL_ENDPOINT
value: "http://otel-collector.monitoring:4317"
- name: ENV
value: "production"
- name: CONTAINER_ID
valueFrom:
fieldRef:
fieldPath: metadata.uid
- name: HOST_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
Docker
Using Docker Compose
Manual Docker Build
Architecture
inapt follows a hexagonal (ports & adapters) architecture:
┌─────────────────────────────────────┐
│ Domain Layer │
│ ┌─────────────────────────────┐ │
│ │ AptRepositoryService │ │
│ │ - Synchronization │ │
│ │ - Metadata generation │ │
│ │ - Package indexing │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
│
┌───────────────┬───────────┼───────────┬───────────────┐
▼ ▼ ▼ ▼ ▼
┌───────────────┐ ┌───────────┐ ┌────────┐ ┌─────────┐ ┌───────────────┐
│ HTTP Server │ │ GitHub │ │ DEB │ │ PGP │ │ SQLite │
│ (Axum) │ │ Client │ │ Reader │ │ Signer │ │ Storage │
└───────────────┘ └───────────┘ └────────┘ └─────────┘ └───────────────┘
Development
# Build
# Run tests
# Run E2E tests (requires Docker)
# Format & lint
# Generate coverage report
Roadmap
High Priority
- Health check endpoint (
/health) for load balancers and Kubernetes probes - Public key endpoint (
/public-key.asc) to serve GPG key directly - ETag/If-None-Match caching against GitHub API for reduced API calls
Medium Priority
- Package version retention policy (keep only N latest versions per package)
- Multiple components support (organize packages beyond
main) - Range proxy mode for private repositories (stream through instead of redirect)
- Package filtering (include/exclude patterns for .deb files)
Low Priority
- PostgreSQL backend option for larger deployments
- Web UI for repository browsing
- Webhook support for immediate sync on new GitHub releases
License
This project is licensed under the AGPL-3.0.
For companies or organizations that wish to use this software in a commercial context without the obligations of the AGPL, a commercial license is available. Please contact us at contact@jdrouet.fr for details.