Skip to content

Latest commit

 

History

71 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Axolotl

Axolotl is the MineSkin proxy used by SkinsRestorer. It forwards authenticated skin and cape requests without exposing the MineSkin API key, waits for queued skin jobs, and converts MineSkin URLs into encrypted skinsrestorer-axolotl:// URLs.

The service is a single Rust binary built with Axum and Tokio. It has no database or persistent local state.

Requirements

  • Rust 1.95.0
  • A MineSkin API key
  • A stable encryption secret

The repository includes rust-toolchain.toml, so Rustup selects the correct toolchain automatically.

Run locally

Set the required environment variables:

export MINESKIN_API_KEY="your-mineskin-api-key"
export AES_SECRET_KEY="a-long-random-secret"

Start the server:

cargo run

Axolotl listens on port 3000 by default. Open Swagger UI or fetch the OpenAPI 3.1 document.

Axolotl also loads variables from a local .env file when one exists.

Configuration

Variable Required Default Purpose
MINESKIN_API_KEY For MineSkin routes None Authenticates outbound MineSkin requests. The Bearer prefix is optional.
AES_SECRET_KEY For encrypted URL routes None Derives the AES-256 key used for existing Axolotl URLs. Keep this value stable across deployments.
DISCORD_WEBHOOK No Disabled HTTPS Discord webhook that receives five-minute status reports.
PORT No 3000 TCP port for the HTTP server. Railpack and Railway provide this automatically.
RUST_LOG No axolotl=info,tower_http=info Controls structured log filtering.

Missing route-specific credentials return a JSON configuration error. The health endpoint remains available without them.

Discord status reports

Set DISCORD_WEBHOOK to enable an operational report every five minutes. Each report includes:

  • Process uptime, RSS memory, and the 1, 5, and 15-minute system load averages.
  • Request totals split by successful responses, redirects, client errors, and server errors.
  • Error rates plus average and histogram-based median and 95th-percentile request latency.
  • Request volume for uploads, jobs, cape operations, URL decryption, health checks, and other routes.
  • MineSkin request totals, errors, rate limits, response bytes, and latency.
  • Unique client IPs, one-request clients, average requests per IP, and the traffic share generated by the busiest IP and busiest five IPs.
  • A traffic pattern label that calls out a dominant client, a few dominant clients, broadly distributed traffic, or a mixed distribution.

Client addresses come from the trusted CF-Connecting-IP header when it is present, then fall back to the direct peer address. Raw addresses stay in memory for aggregation and are never included in the Discord payload. Axolotl tracks up to 100,000 client addresses in an unsent reporting window. Requests above that limit still count toward request totals but appear as unattributed traffic in the client breakdown.

Metrics advance only after Discord accepts a report. If delivery fails, the next attempt includes the full unsent period instead of discarding counters. The reporting window is stored in memory and resets when the process restarts.

API

Method Path Purpose
GET /health Report service health.
POST /mineskin/skins Upload a skin and wait for its MineSkin job to complete.
GET /mineskin/jobs/{jobId} Fetch and sanitize a MineSkin job.
GET /mineskin/capes List supported capes. Results are cached for five minutes.
GET /mineskin/cape-support Check the configured account's cape grant and list usable capes.
GET /mineskin/decrypt-url Decrypt an Axolotl URL into its original https://minesk.in/ URL.

Skin uploads accept up to 5 MiB. Axolotl processes up to 16 uploads at once and returns 503 when every upload slot is occupied. Upload bodies and complete upload workflows have deadlines, while MineSkin polling can run for at most five minutes and returns 504 if processing does not finish in time.

New encrypted URLs use an authenticated v2 payload. The decryption endpoint continues to accept URLs created by the legacy AES-CBC service.

Deploy with Railpack

Railpack detects the root Cargo.toml, selects Rust 1.95.0 from rust-toolchain.toml, builds the release binary, and starts ./bin/axolotl. No custom build or start command is required.

Configure MINESKIN_API_KEY and AES_SECRET_KEY in the deployment environment, then deploy the repository. On Railway, Axolotl listens on the injected PORT and exposes /health for deployment health checks.

Run the published image

Pushes to main build a release image with Railpack and publish it to ghcr.io/skinsrestorer/axolotl. Each build publishes three tags:

  • latest: the latest published build from main
  • main: the latest published build from main
  • sha-<12-character-commit-sha>: the build for a specific commit

The workflow uses the ubuntu-24.04 runner and authenticates with GITHUB_TOKEN, with packages: write permission.

Put the required environment variables in a local .env file. Then run:

docker run -d --name axolotl --restart unless-stopped \
  --env-file .env -p 3000:3000 ghcr.io/skinsrestorer/axolotl:latest

The service is available at http://localhost:3000.

Validate changes

Run the same checks used by CI:

cargo fmt --all -- --check
cargo clippy --all-targets --all-features --locked -- -D warnings
cargo test --all-targets --all-features --locked
cargo build --release --locked

The tests include a fixed compatibility vector from the previous service. This protects existing encrypted URLs while the implementation remains in Rust.

About

🎨 MineSkin API proxy for SkinsRestorer

Resources

Stars

0 stars

Watchers

0 watching

Forks

Sponsor this project

Packages

Contributors

Languages