Files
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

173 lines
6.0 KiB
Go

// Package models defines the domain types used across urapt's database and API
// boundaries. These structs mirror the SQLite schema and are serialized into
// API DTOs by the restapi and apiclient packages.
package models
// User is an urapt account.
type User struct {
ID string `json:"id"`
Username string `json:"username"`
IsAdmin bool `json:"is_admin"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// APIToken is a revocable authentication token. The plaintext token is only
// returned at creation/login time; only its hash and a short display prefix
// are persisted.
type APIToken struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Name string `json:"name"`
Prefix string `json:"prefix"`
Token string `json:"token,omitempty"` // plaintext, only on creation
CreatedAt string `json:"created_at"`
LastUsedAt *string `json:"last_used_at,omitempty"`
RevokedAt *string `json:"revoked_at,omitempty"`
}
// Visibility is whether a repository is world-readable.
type Visibility string
const (
VisibilityPublic Visibility = "public"
VisibilityPrivate Visibility = "private"
)
// Access is a user's role on a repository.
type Access string
const (
AccessRead Access = "read"
AccessWrite Access = "write"
AccessReadWrite Access = "read-write"
AccessAdmin Access = "admin"
)
// ValidAccess reports whether s is a recognized access level.
func ValidAccess(s string) bool {
switch Access(s) {
case AccessRead, AccessWrite, AccessReadWrite, AccessAdmin:
return true
}
return false
}
// Repository is a named APT repository owned by a user.
type Repository struct {
ID string `json:"id"`
Name string `json:"name"`
OwnerUserID string `json:"owner_user_id"`
Owner *User `json:"owner,omitempty"`
Visibility Visibility `json:"visibility"`
Description string `json:"description"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// RepositoryMember is a user's access grant on a repository.
type RepositoryMember struct {
RepositoryID string `json:"repository_id"`
UserID string `json:"user_id"`
User *User `json:"user,omitempty"`
Access Access `json:"access"`
CreatedAt string `json:"created_at"`
}
// Distribution (suite) within a repository.
type Distribution struct {
ID string `json:"id"`
RepositoryID string `json:"repository_id"`
Name string `json:"name"`
CreatedAt string `json:"created_at"`
}
// Component within a distribution (e.g. main, contrib).
type Component struct {
ID string `json:"id"`
DistributionID string `json:"distribution_id"`
Name string `json:"name"`
CreatedAt string `json:"created_at"`
}
// Architecture configured for a distribution (e.g. amd64). The special
// architecture "all" is implicit and never stored.
type Architecture struct {
ID string `json:"id"`
DistributionID string `json:"distribution_id"`
Name string `json:"name"`
CreatedAt string `json:"created_at"`
}
// Package is one uploaded .deb version and its extracted control metadata.
type Package struct {
ID string `json:"id"`
RepositoryID string `json:"repository_id"`
DistributionID string `json:"distribution_id"`
ComponentID string `json:"component_id"`
Name string `json:"name"`
Version string `json:"version"`
Architecture string `json:"architecture"`
Source string `json:"source,omitempty"`
Maintainer string `json:"maintainer,omitempty"`
Priority string `json:"priority,omitempty"`
Section string `json:"section,omitempty"`
Origin string `json:"origin,omitempty"`
Homepage string `json:"homepage,omitempty"`
Description string `json:"description,omitempty"`
DescriptionMD5 string `json:"description_md5,omitempty"`
Depends string `json:"depends,omitempty"`
PreDepends string `json:"pre_depends,omitempty"`
Recommends string `json:"recommends,omitempty"`
Suggests string `json:"suggests,omitempty"`
Conflicts string `json:"conflicts,omitempty"`
Breaks string `json:"breaks,omitempty"`
Provides string `json:"provides,omitempty"`
Replaces string `json:"replaces,omitempty"`
Enhances string `json:"enhances,omitempty"`
InstalledSize int64 `json:"installed_size,omitempty"`
Essential string `json:"essential,omitempty"`
BuiltUsing string `json:"built_using,omitempty"`
Tag string `json:"tag,omitempty"`
RawControl string `json:"raw_control"`
Filename string `json:"filename"`
PoolPath string `json:"pool_path"`
Size int64 `json:"size"`
MD5sum string `json:"md5sum"`
SHA1 string `json:"sha1"`
SHA256 string `json:"sha256"`
UploadedByUserID string `json:"uploaded_by_user_id"`
CreatedAt string `json:"created_at"`
}
// Blob is a content-addressed .deb file on disk, reference-counted for dedup.
type Blob struct {
SHA256 string `json:"sha256"`
Filename string `json:"filename"`
Size int64 `json:"size"`
RefCount int64 `json:"ref_count"`
CreatedAt string `json:"created_at"`
}
// GPGKey is a server-managed signing key.
type GPGKey struct {
ID string `json:"id"`
Fingerprint string `json:"fingerprint"`
UserID string `json:"user_id"`
PublicKeyArmored string `json:"public_key_armored"`
PrivateKeyArmored string `json:"-"` // never serialized in API responses
IsDefault bool `json:"is_default"`
CreatedAt string `json:"created_at"`
}
// AuditLogEntry is a best-effort record of a mutating action.
type AuditLogEntry struct {
ID string `json:"id"`
UserID *string `json:"user_id,omitempty"`
RepositoryID *string `json:"repository_id,omitempty"`
Action string `json:"action"`
Target string `json:"target"`
Details string `json:"details,omitempty"`
CreatedAt string `json:"created_at"`
}