ListReposVisible, GetRepositoryByName, and GetRepositoryByID left Repository.Owner as nil because they only selected repository columns. LEFT JOIN users in all three and populate Owner, so the API (and the web UI's repositories table) can show the owner's username.
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
make build
./urapt-server --bind 0.0.0.0:8080 --base-url https://apt.example.com
Or with Docker:
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
./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
./urapt push myrepo stable main ./hello_1.0.0_amd64.deb
Configure apt clients
./urapt apt-config myrepo stable
This prints the exact commands to install the signing key and add the repository, for example:
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
.debfiles 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/**— servesdists/.../{Release,InRelease, Release.gpg},Packages[.gz|.xz], andpool/.../*.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.
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. See LICENSE for the
full text.