Files
urapt/README.md
T
owen 981587e83d Initial release: self-hostable APT repository server and CLI
urapt is a self-hostable APT repository server with a companion CLI for
pushing and managing Debian .deb packages.

Server (urapt-server):
- REST API + APT endpoint, SQLite storage (pure-Go modernc driver, no CGO)
- .deb files stored content-addressed on disk, reference-counted for dedup
- Server-managed RSA-4096 OpenPGP signing key (ProtonMail/go-crypto)
- APT indices (Release/InRelease/Packages[.gz/.xz]) generated on demand
  from the DB, cached in memory, signed with the server key
- Full APT model: repositories -> distributions -> components -> architectures
- Bearer-token auth for REST; HTTP Basic auth for private-repo APT reads
- First registrant becomes admin; repo-scoped permissions
  (read/write/read-write/admin) plus owner and server-admin roles
- Multipart package push with control-field extraction, list/show/delete,
  pool serving, blob ref-count cleanup
- Audit log

CLI (urapt):
- register/login/logout/whoami, token management
- repo/distro/component/arch CRUD, member management
- push/pull/ls/show/rm for packages
- apt-config helper that emits apt setup commands (key, sources.list,
  auth.conf for private repos)

Packaging & docs:
- Dockerfile (multi-stage distroless), docker-compose.yml, sample config
- README quick start, architecture overview, config reference, security notes
- PLAN.md design blueprint, CHANGELOG.md, GPL-3.0 LICENSE
- GitHub Actions CI (test, lint, cross-build for linux/darwin amd64/arm64)
- Makefile release target producing static binaries + tarballs + checksums

Tests cover the data-access layer, auth/permission checks, APT index
generation, .deb parsing, GPG signing, the REST API, and the typed API
client. Verified end-to-end on a Raspberry Pi (arm64) pushing and installing
a real package.
2026-06-28 16:57:34 -05:00

160 lines
5.7 KiB
Markdown

# urapt
A self-hostable APT repository server with a companion CLI for pushing and
managing Debian/Ubuntu `.deb` packages under your logged-in user.
urapt gives you your own `apt` server: run the server, log in with the CLI,
create repositories, and push `.deb` files. Clients configure `apt` against it
and install packages normally. Packages are stored as content-addressed files
on disk; everything else lives in a SQLite database. APT indices
(`Release`, `InRelease`, `Packages`) are generated on demand from the database
and signed with a server-managed OpenPGP key.
## Components
- **`urapt-server`** (`cmd/urapt-server`) — the REST API + APT endpoint server.
- **`urapt`** (`cmd/urapt`) — the CLI for pushing packages and managing repos.
- **`shared/`** — shared utilities (config, db, models, gpg, deb parsing, apt
index generation, the typed API client) used by both server and CLI.
## Quick start
### Run the server
```bash
make build
./urapt-server --bind 0.0.0.0:8080 --base-url https://apt.example.com
```
Or with Docker:
```bash
docker compose up -d # see docker-compose.yml
```
The server creates `store/database/sqlite.db` and `store/packages/` on first
run and generates an RSA-4096 signing key stored in the database.
### Set up the CLI
```bash
./urapt register https://apt.example.com # first account becomes admin
./urapt repo create myrepo --public
./urapt distro create myrepo stable
./urapt component create myrepo stable main
./urapt arch add myrepo stable amd64
```
### Push a package
```bash
./urapt push myrepo stable main ./hello_1.0.0_amd64.deb
```
### Configure apt clients
```bash
./urapt apt-config myrepo stable
```
This prints the exact commands to install the signing key and add the
repository, for example:
```bash
curl -fsSL https://apt.example.com/api/v1/server/pubkey \
| sudo gpg --dearmor -o /usr/share/keyrings/urapt-myrepo.gpg
echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/urapt-myrepo.gpg] https://apt.example.com/apt/myrepo/ stable main' \
| sudo tee /etc/apt/sources.list.d/myrepo.list
sudo apt update
sudo apt install hello
```
For **private** repositories, `apt-config` also prints an
`/etc/apt/auth.conf.d/...` snippet using your API token as the password.
## Architecture
```
+-------------------+ +-------------------+
| urapt (CLI) | | urapt-server |
+---------+---------+ +---------+---------+
| shared/ | shared/
v v
+---------------------------------------------+
| shared/ |
| config | db | models | gpg | deb | apt | |
| crypto | api(DTOs) | apiclient | httputil |
+---------------------------------------------+
| |
+--> SQLite <---+ store/database/sqlite.db
store/packages/<sha256>.deb (files only)
```
- The CLI never touches the DB or filesystem; it only talks to the REST API.
- Only uploaded `.deb` files are stored on disk (`store/packages/`), content-
addressed by SHA-256 and reference-counted for deduplication.
- APT indices are generated in memory from the DB and cached (invalidated on
any mutation); they are never written to disk.
### Endpoints
- **REST API** at `/api/v1/**` — auth, users, repositories, members,
distributions/components/architectures, packages. Bearer-token auth.
- **APT endpoint** at `/apt/:repo/**` — serves `dists/.../{Release,InRelease,
Release.gpg}`, `Packages[.gz|.xz]`, and `pool/.../*.deb`. Public repos allow
anonymous reads; private repos require HTTP Basic auth (password = API token).
### Permissions
Each repository has an owner with full access. The owner can grant `read`,
`write`, `read-write`, or `admin` to other users. Only users with access can
push. Server admins can manage everything.
## Configuration
Server config is loaded from defaults → TOML file (`--config`, default
`./urapt-server.toml`) → environment (`URAPT_*`) → flags. Key options:
| Key | Default | Notes |
|---|---|---|
| `bind` | `0.0.0.0:8080` | listen address |
| `base_url` | `http://localhost:8080` | external URL for apt-config output |
| `store_dir` | `./store` | data directory |
| `signing_key_bits` | `4096` | RSA signing key size |
| `max_package_size` | `1073741824` | 1 GiB upload limit |
| `open_registration` | `true` | allow new account registration |
## Building
Requires Go 1.22+. CGO is not required (SQLite is the pure-Go `modernc`
driver), so binaries are static and cross-compilable.
```bash
make build # builds urapt-server and urapt
make test # go test ./...
make vet # go vet ./...
```
## Security notes
- Passwords are bcrypt-hashed; API tokens are random 32-byte values stored only
as SHA-256 hashes (revocable, with a display prefix).
- The OpenPGP **private signing key is stored unencrypted in the SQLite
database**. This is acceptable when you control the database file; for
stronger protection, restrict file permissions and back up the DB securely.
Per-repo keys and key encryption-at-rest are planned.
- For internet-facing deployments, run behind a TLS-terminating reverse proxy
(Caddy/nginx). Private-repo credentials must never travel over plain HTTP.
## Status
See `CHANGELOG.md` for release history and `PLAN.md` for the full design and
roadmap. Future work includes per-repo signing keys, key encryption-at-rest,
OS keychain token storage, source packages, AppStream metadata, and a web UI.
## License
urapt is free software released under the terms of the
[GNU General Public License v3.0 or later](LICENSE). See `LICENSE` for the
full text.