Skip to content

laputa-systems/mirror

Repository files navigation

Laputa Mirror

laputa-mirror serves the Laputa PM repository format from an S3-compatible bucket and exposes authenticated PUT publishing for:

  • index.json
  • packages/<arch>/<name>/<name>-<ver>-<rel>.tar.gz
  • metadata/<arch>/<name>/<name>-<ver>-<rel>.json
  • sources/<name>/<name>-<ver>-<rel>-src.tar.gz

The mirror accepts the older packages/<name>/... object path for existing arm64 packages, but new PM uploads use the arch-qualified path. Source mirrors stay shared across architectures.

This guide stands up a new mirror at https://laputa.17166969.xyz/ using Cloudflare R2 for object storage and a Cloudflare Tunnel for the only public origin path. With this tunnel-only setup, leave R2_PUBLIC_URL unset so package downloads are served through laputa-mirror instead of redirecting clients to a public R2 URL.

Architecture

Laputa PM clients
  -> https://laputa.17166969.xyz
  -> Cloudflare Tunnel
  -> cloudflared on the server
  -> http://127.0.0.1:3000
  -> laputa-mirror
  -> Cloudflare R2 bucket: laputa-mirror

The server does not need inbound ports open. cloudflared makes outbound connections to Cloudflare and forwards the public hostname to the local mirror.

Build Machine Prerequisites

On the machine where you build the .deb:

sudo apt-get update
sudo apt-get install -y build-essential curl pkg-config libssl-dev nodejs npm dpkg-dev

Install Rust with rustup if it is not already present:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
. "$HOME/.cargo/env"
rustup default stable

Install cloudflared using Cloudflare's package instructions for Debian/Ubuntu, or install the binary at /usr/bin/cloudflared on the server.

R2 Bucket

You already created the bucket:

laputa-mirror

Create an R2 API token with object read/write access to that bucket. You need:

S3_ENDPOINT=https://<account_id>.r2.cloudflarestorage.com
S3_BUCKET=laputa-mirror
S3_ACCESS_KEY_ID=<r2_access_key_id>
S3_SECRET_ACCESS_KEY=<r2_secret_access_key>
S3_REGION=auto

Do not configure R2_PUBLIC_URL for the tunnel-only setup.

Build The Debian Package

make deb builds debug x86_64-unknown-linux-musl binaries and packages them as amd64. It requires cargo-zigbuild:

cd /path/to/laputa-systems/mirror
cargo install cargo-zigbuild
make deb

Output:

laputa-mirror_0.1.0_amd64.deb

The package includes:

  • /usr/bin/laputa-mirror
  • /usr/bin/laputa-mirror-publish
  • /usr/share/laputa-mirror/static
  • /lib/systemd/system/laputa-mirror.service
  • /etc/laputa-mirror/env.example

The repository intentionally does not build release binaries from this target.

Copy the package to the server:

scp laputa-mirror_0.1.0_amd64.deb root@<server>:/tmp/

Install The Mirror Service On The Server

Install the package:

sudo dpkg -i /tmp/laputa-mirror_0.1.0_amd64.deb

The package post-install creates the laputa-mirror service user, creates /var/lib/laputa-mirror, and copies /etc/laputa-mirror/env.example to /etc/laputa-mirror/env if that file does not already exist.

Edit the env file:

sudo editor /etc/laputa-mirror/env

Minimum tunnel-only config:

LISTEN_ADDR=127.0.0.1:3000

S3_ENDPOINT=https://<account_id>.r2.cloudflarestorage.com
S3_BUCKET=laputa-mirror
S3_ACCESS_KEY_ID=<r2_access_key_id>
S3_SECRET_ACCESS_KEY=<r2_secret_access_key>
S3_REGION=auto

DB_PATH=/var/lib/laputa-mirror/auth.db
ALLOWED_USERS=josh
RP_ID=laputa.17166969.xyz
RP_ORIGIN=https://laputa.17166969.xyz

Set ALLOWED_USERS to the usernames allowed to register passkeys. Comma separate multiple users.

Start the local service:

sudo systemctl daemon-reload
sudo systemctl enable --now laputa-mirror
sudo systemctl status laputa-mirror

Local check:

curl -fsSL http://127.0.0.1:3000/health

Create The Cloudflare Tunnel

This guide uses a locally-managed tunnel because it is easy to reproduce from the CLI. Cloudflare recommends remotely-managed tunnels for most production deployments; the origin service and hostname are the same either way.

Authenticate cloudflared:

cloudflared tunnel login

Create the tunnel:

cloudflared tunnel create laputa-mirror

Record the tunnel UUID from the output. The command also creates:

~/.cloudflared/<tunnel-uuid>.json

Install the tunnel credentials:

export TUNNEL_ID=<tunnel-uuid>

sudo install -d -m 755 /etc/cloudflared
sudo install -m 600 "$HOME/.cloudflared/$TUNNEL_ID.json" "/etc/cloudflared/$TUNNEL_ID.json"

Create /etc/cloudflared/config.yml:

sudo tee /etc/cloudflared/config.yml >/dev/null <<EOF
tunnel: $TUNNEL_ID
credentials-file: /etc/cloudflared/$TUNNEL_ID.json

ingress:
  - hostname: laputa.17166969.xyz
    service: http://127.0.0.1:3000
  - service: http_status:404
EOF

Create the DNS route:

cloudflared tunnel route dns laputa-mirror laputa.17166969.xyz

Install and start cloudflared as a service:

sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared

Public check:

curl -fsSL https://laputa.17166969.xyz/health

Create The First Publisher Token

Open:

https://laputa.17166969.xyz/auth

Register a passkey using a username from ALLOWED_USERS.

Then open:

https://laputa.17166969.xyz/auth/settings

Create a named token, for example github-actions, and store it once. Tokens are shown only at creation time.

Publish The Bootstrap Repo

Set the publisher environment:

export LAPUTA_MIRROR_URL=https://laputa.17166969.xyz
export LAPUTA_MIRROR_TOKEN=<token_from_settings>

Publish an existing exported PM repo:

cargo run --bin laputa-mirror-publish -- \
  .out/laputa-bootstrap-build-essential-native-repo

The publisher:

  1. Uploads every package tarball from packages/<arch>/.
  2. Uploads package metadata sidecars from metadata/<arch>/.
  3. Maps .out/source-mirrors/<pkg>-<ver>-<rel>.tar.gz to sources/<pkg>/<pkg>-<ver>-<rel>-src.tar.gz.
  4. Recomputes package/source sha256 and package size metadata.
  5. Uploads index.json last.

Large package and source uploads are split into smaller chunk requests to avoid Cloudflare Tunnel request body limits. The publisher only talks to laputa-mirror; the mirror service is the only component that writes to R2.

Verify:

curl -fsSL https://laputa.17166969.xyz/index.json | jq '.[].name'
curl -I https://laputa.17166969.xyz/packages/aarch64/build-essential-native/build-essential-native-1-2.tar.gz
curl -I https://laputa.17166969.xyz/metadata/aarch64/build-essential-native/build-essential-native-1-2.json
curl -I https://laputa.17166969.xyz/sources/linux/linux-7.0.5-5-src.tar.gz

GitHub Actions Publishing

Add repository secrets:

LAPUTA_MIRROR_TOKEN=<token_from_settings>

Run the manual workflow:

Laputa Mirror Publish ARM64

Use the workflow run ID that produced:

laputa-bootstrap-build-essential-native-repo-arm64

The workflow downloads that artifact, validates the expected repo layout, publishes it, then checks the public index.json and a known package URL.

Configure PM Clients

Use the mirror as the public repo:

export XSH_PM_PUBLIC_REPO=https://laputa.17166969.xyz

For uploads from local PM tooling, use:

export XSH_PM_REPO=https://laputa.17166969.xyz
export LAPUTA_TOKEN=<token_from_settings>

Operations

Check services:

sudo systemctl status laputa-mirror
sudo systemctl status cloudflared

Logs:

sudo journalctl -u laputa-mirror -f
sudo journalctl -u cloudflared -f

Restart after env changes:

sudo systemctl restart laputa-mirror

Restart after tunnel config changes:

sudo systemctl restart cloudflared

Notes

  • Keep /etc/laputa-mirror/env mode 0600; it contains R2 credentials.
  • Keep /etc/cloudflared/<tunnel-uuid>.json mode 0600; it is the tunnel credential.
  • Large package/source uploads are chunked through laputa-mirror; CI does not receive or use R2 credentials.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors