Skip to content

Latest commit

 

History

History
240 lines (160 loc) · 7.91 KB

File metadata and controls

240 lines (160 loc) · 7.91 KB

API Reference

All endpoints are served on the same port as the frontend (default 3001).

Authentication

Write endpoints require a token when AUTH_TOKEN is configured on the server. Pass it as either:

Authorization: Bearer <token>

or

X-Auth-Token: <token>

If AUTH_TOKEN is empty, all write endpoints are open. Read endpoints are always public.

Rate limiting & brute-force protection

All limits are per IP:

Bucket Endpoints Default limit
Upload POST /api/dump/upload, POST /api/dump/import 10 / 10 s
Delete-by-key GET/POST /api/delete/:key 10 / 10 s
General GET /api/dump/:id, …/manifest, GET /api/dumps, DELETE /api/dump/:id 100 / 10 s (GENERAL_RATE_LIMIT)
Modpack GET /api/dump/:id/modpack 10 / 60 s (MODPACK_RATE_LIMIT)

Token-gated endpoints additionally share an auth-failure budget: only requests answered with 401 count, so legitimate use is never throttled. After AUTH_FAIL_LIMIT failures (default 10) within 15 minutes, the IP receives 429 for all token-gated requests — even with a valid token — until the window expires. Failed auth attempts are also delayed by AUTH_FAIL_DELAY_MS (default 500 ms) before the 401 is sent.


GET /health

Health check.

Response 200: { "ok": true }


POST /api/dump/upload

Upload a dump zip from disk.

  • Auth: required if AUTH_TOKEN is set
  • Content-Type: multipart/form-data
  • Field: file — the .zip file (max 512 MB)
  • Field: ttl (optional) — how long the dump survives on the server, in seconds. Max 31536000 (1 year). Defaults to 1 year.

Response 200:

{ "id": "550e8400-e29b-41d4-a716-446655440000", "deleteKey": "<base64url-encoded key>" }

The id is the manifest_id from manifest.json inside the zip (UUID v4). The deleteKey can be used with GET/POST /api/delete/:key to delete the dump later without the server auth token.

curl example:

# Upload with default TTL (1 year)
curl -X POST http://localhost:3001/api/dump/upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@my-dump.zip"

# Upload with a 7-day TTL
curl -X POST http://localhost:3001/api/dump/upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@my-dump.zip" \
  -F "ttl=604800"

POST /api/dump/import

Fetch a dump zip from a remote URL and store it on the server.

  • Auth: required if AUTH_TOKEN is set
  • Content-Type: application/json
  • Body: { "url": "https://example.com/dump.zip", "ttl": 604800 }
    • url — required. Must be a public http/https address. Private and loopback addresses are blocked (SSRF protection). Up to 5 HTTP redirects are followed; each redirect target is validated against the same rules. Max size: 512 MB.
    • ttl (optional) — how long the dump survives on the server, in seconds. Max 31536000 (1 year). Defaults to 1 year.

Response 200:

{ "id": "550e8400-e29b-41d4-a716-446655440000", "deleteKey": "<base64url-encoded key>" }

curl example:

# Import with a 24-hour TTL
curl -X POST http://localhost:3001/api/dump/import \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/dump.zip", "ttl": 86400}'

GET /api/dump/:id

Download the raw zip for a stored dump. Always public — no auth required.

GET /api/dump/550e8400-e29b-41d4-a716-446655440000

Returns the zip with Content-Type: application/zip. Returns 404 if no dump with that id exists, 400 if the id is not a valid UUID v4.

Response headers:

Header Description
X-Expires-At ISO 8601 timestamp when the dump will be deleted (e.g. 2027-03-16T12:00:00.000Z). Present only when a TTL sidecar exists.
Expires Same timestamp in HTTP-date format, for caches. Present only when a TTL sidecar exists.

GET /api/dump/:id/manifest

Return the parsed manifest.json from a stored dump as JSON. Always public — no auth required.

GET /api/dump/550e8400-e29b-41d4-a716-446655440000/manifest

Returns the raw manifest object, including the hashes field for v2 dumps. Useful for programmatic access (e.g. building Modrinth/CurseForge/Prism modpacks).

Response 200:

{
  "manifest_version": 2,
  "manifest_id": "550e8400-e29b-41d4-a716-446655440000",
  "versions": { "skyblockbuilder": "2.0", "minecraft": "1.21.1" },
  "hashes": {
    "somemod": { "md5": "abc123", "sha1": "def456", "sha512": "ghi789" }
  },
  "files": []
}

Returns 404 if no dump with that id exists, 400 if the id is not a valid UUID v4.


DELETE /api/dump/:id

Delete a stored dump.

  • Auth: required if AUTH_TOKEN is set

Response: 204 No Content on success.

curl example:

curl -X DELETE http://localhost:3001/api/dump/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer $TOKEN"

GET /api/delete/:key

Show a confirmation page for deleting a stored dump using the delete key returned by the upload or import endpoint. No auth token required — the key itself is the credential. Rate limited.

GET /api/delete/<deleteKey>

The key encodes the dump id using the server's RSA private key. The server recovers the id from the key and, if the dump exists, returns an HTML page with a delete button. The actual deletion happens via POST /api/delete/:key (the page's form submits to the same URL), so opening the link — or a link prefetcher following it — never deletes anything by itself.

Response 200: HTML confirmation page

Response 400: Invalid delete key — key could not be decoded or did not contain a valid id

Response 404: Not found — key is valid but the dump no longer exists (already deleted or expired)


POST /api/delete/:key

Delete a stored dump using its delete key. No auth token required — the key itself is the credential. Rate limited.

Response 200: Deleted (plain text)

Response 400: Invalid delete key — key could not be decoded or did not contain a valid id

Response 404: Not found — key is valid but the dump no longer exists (already deleted or expired)

curl example:

curl -X POST http://localhost:3001/api/delete/<deleteKey>

GET /api/dumps

List all stored dumps, sorted newest first.

  • Auth: required if AUTH_TOKEN is set

Response 200:

{
  "dumps": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "size": 4096,
      "createdAt": "2025-01-01T00:00:00.000Z",
      "expiresAt": "2026-01-01T00:00:00.000Z",
      "manifestVersion": 2,
      "versions": {
        "skyblockbuilder": "2.0",
        "minecraft": "1.21.1",
        "neoforge": "21.1.80"
      }
    }
  ]
}

manifestVersion is the dump's manifest format version and versions is the full versions object from the manifest (all mod versions, stored verbatim). Both are null if the zip or its manifest is unreadable. The fields are recorded in the .meta sidecar at upload time; for dumps stored before this field existed they are extracted from the zip and backfilled into the sidecar on the first listing.

curl example:

curl http://localhost:3001/api/dumps \
  -H "Authorization: Bearer $TOKEN"