owen bff58fd842 Populate Repository.Owner in list/get store queries
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.
2026-06-28 18:42:31 -05:00

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 .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.

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.

S
Description
Your personal apt repository server, written in Go (because AI models can't read the word "Rust" I geuss 🤷‍♂️)
Readme GPL-3.0
287 KiB
Languages
Go 63%
TypeScript 35.2%
JavaScript 0.6%
Makefile 0.5%
CSS 0.3%
Other 0.3%