| Crates.io | inapt |
| lib.rs | inapt |
| version | 0.3.2 |
| created_at | 2025-09-29 18:36:09.63429+00 |
| updated_at | 2026-01-23 09:20:17.640474+00 |
| description | A minimal Debian/Ubuntu APT repository proxy written in Rust. Exposes a valid APT repo structure over HTTP, sourcing .deb packages from GitHub Releases. |
| homepage | |
| repository | https://github.com/jdrouet/inapt |
| max_upload_size | |
| id | 1860000 |
| size | 1,167,392 |
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.
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 InRelease and Release.gpg
By-Hash Support: Implements Acquire-By-Hash for atomic index updates
Translation Files: Serves Translation-en files with package descriptions in multiple compression formats (gzip, bzip2, xz)
GitHub Integration:
.deb downloads to GitHub (leverages GitHub CDN)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
cargo run --bin inapt-genkey > resources/private-key.pem
Extract the public key to share with users:
# The public key is printed first in the output
head -n 20 resources/private-key.pem > public-key.asc
Create a config.toml file:
[core]
origin = "My Repository"
label = "Debian"
suite = "stable"
version = "1.0.0"
codename = "cucumber"
description = "Packages from GitHub releases"
repositories = ["myorg/myproject", "myorg/another-project"]
[github]
# Optional: for private repos or higher rate limits
# token = "ghp_..."
[http_server]
address = "0.0.0.0"
port = 3000
[pgp_cipher]
private_key_path = "resources/private-key.pem"
# passphrase = "optional-passphrase"
[sqlite]
path = "inapt.db"
[worker]
interval = 3600 # Sync every hour (default: 43200 = 12 hours)
cargo run
# Or with custom config path:
CONFIG_PATH=/path/to/config.toml cargo run
The proxy listens on 0.0.0.0:3000 by default.
Add the repository and GPG key:
# Add the GPG key
curl -fsSL http://localhost:3000/public-key.asc | sudo gpg --dearmor -o /usr/share/keyrings/inapt.gpg
# Add the repository
echo "deb [signed-by=/usr/share/keyrings/inapt.gpg] http://localhost:3000 stable main" | \
sudo tee /etc/apt/sources.list.d/inapt.list
# Update and install
sudo apt update
sudo apt install mypackage
For testing without signature verification (not recommended for production):
echo "deb [trusted=yes] http://localhost:3000 stable main" | \
sudo tee /etc/apt/sources.list.d/inapt.list
| 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) |
| 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 |
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) |
TRACING_MODE=console TRACING_LEVEL=debug cargo run
# Start Jaeger
docker run -d --name jaeger \
-p 4317:4317 \
-p 16686:16686 \
jaegertracing/all-in-one:latest
# Run inapt with OTLP export
TRACING_MODE=otel \
TRACING_OTEL_ENDPOINT=http://localhost:4317 \
ENV=development \
cargo run
# View traces at http://localhost:16686
# 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-compose up
docker build -t inapt .
docker run -p 3000:3000 \
-v ./config.toml:/etc/inapt/config.toml \
-v ./inapt.db:/data/inapt.db \
-v ./resources/private-key.pem:/etc/inapt/private-key.pem \
inapt
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 │
└───────────────┘ └───────────┘ └────────┘ └─────────┘ └───────────────┘
# Build
cargo build
# Run tests
cargo test
# Run E2E tests (requires Docker)
cargo test --test e2e_apt_repository -- --ignored
# Format & lint
cargo fmt
cargo clippy
# Generate coverage report
cargo llvm-cov
/health) for load balancers and Kubernetes probes/public-key.asc) to serve GPG key directlymain)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.