Scaffolds the workspace per architecture/generic.md §1 and implements
phases 0-3 of doc/spec.md §12.
Crates:
entities manifest/ref/repo schemas, bucket key layout, config, the
X-Error-Code taxonomy. No I/O.
core resolver, freeze pinning, single-flight, the streaming tee,
policy, gc/verify/refresh. Defines the Store and Upstream
ports.
data object_store (S3 + local) and reqwest Hub adapters.
api the axum surface: resolve, model/dataset info, tree, refs,
whoami, metrics, bearer auth, range handling.
bin one binary: serve plus fetch/pin/refresh/list/show/rm/gc/
verify/doctor.
Deployment targets bob.hanzalova.internal:20482 (port derived per
architecture/port-allocations.md §3), storing to the MinIO on
caveman.kosherinata.internal, fronted by hanzalova at rf.internal.
Ships the sysusers drop-in, hardened unit, firewalld service, nginx
vhost, config template, infra-setup.sh and the Gitea Actions
ci/deploy/conformance workflows.
Testing: 112 unit and integration tests, including the sovereignty
suite (cold fetch, sealed replay, single-flight, client disconnect,
range resume, freeze stability, gc-after-rm, digest mismatch), plus a
conformance suite driving a pinned huggingface_hub against a real Hub.
Deviations from the spec, all deliberate:
- one binary with subcommands (spec §9) rather than generic.md's
separate -api and -cli binaries; the library split is unchanged.
- a dedicated sysusers account and hardened unit (generic.md §8)
rather than the spec's illustrative DynamicUser unit.
- manifests carry an optional repo_tree recorded verbatim, resolving
spec §13's "record whole, filter on read" question for the tree
endpoint as well as model-info.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZG2i4AmfSqE97EJGBVb64
60 lines
1.9 KiB
Desktop File
60 lines
1.9 KiB
Desktop File
[Unit]
|
|
Description=rustingface sovereign model registry
|
|
Documentation=https://git.lair.cafe/grenade/rustingface
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
# Type=exec, not notify: the binary sends no sd_notify, and a notify unit that
|
|
# never sends READY=1 blocks `systemctl restart` until TimeoutStartSec.
|
|
Type=exec
|
|
User=rustingface
|
|
Group=rustingface
|
|
ExecStart=/usr/local/bin/rustingface serve --config /etc/rustingface/config.toml
|
|
Restart=on-failure
|
|
RestartSec=5s
|
|
|
|
# Credentials are passed by systemd rather than read from disk by the service,
|
|
# so the secret files stay root-owned and never appear in the unit's namespace
|
|
# except under $CREDENTIALS_DIRECTORY. The config names these basenames.
|
|
LoadCredential=s3-access-key:/etc/rustingface/s3-access-key
|
|
LoadCredential=s3-secret-key:/etc/rustingface/s3-secret-key
|
|
LoadCredential=hf-token:/etc/rustingface/hf-token
|
|
|
|
# Long transfers must not be killed mid-flight on stop; a detached upload that
|
|
# is cut short leaves an orphan blob, which is safe but wasteful.
|
|
TimeoutStopSec=120
|
|
|
|
# Hardening. The process holds no durable state — the bucket is the whole of it
|
|
# — so there is no StateDirectory to protect and ProtectSystem can stay strict
|
|
# with no ReadWritePaths at all.
|
|
NoNewPrivileges=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
PrivateTmp=true
|
|
PrivateDevices=true
|
|
ProtectKernelTunables=true
|
|
ProtectKernelModules=true
|
|
ProtectControlGroups=true
|
|
ProtectClock=true
|
|
ProtectHostname=true
|
|
ProtectProc=invisible
|
|
RestrictRealtime=true
|
|
RestrictSUIDSGID=true
|
|
RestrictNamespaces=true
|
|
LockPersonality=true
|
|
MemoryDenyWriteExecute=true
|
|
SystemCallFilter=@system-service
|
|
SystemCallArchitectures=native
|
|
CapabilityBoundingSet=
|
|
AmbientCapabilities=
|
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
|
UMask=0077
|
|
|
|
# Concurrent multi-gigabyte transfers each hold a handful of sockets and part
|
|
# buffers; the default 1024 is comfortable but the headroom costs nothing.
|
|
LimitNOFILE=16384
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|