rebreak-monorepo/backend/MAGIC_API.md
chahinebrini 77edd67cbe fix(magic): explicit imports + staging defaults + sheet height
- backend/api/magic/register: explicit import of MAGIC_DEVICE_LIMIT
  and createAdGuardClient (Nitro auto-import was missing them
  → ReferenceError → HTTP 500 on /api/magic/register)
- mac-app: default backendBaseUrl falls back to staging.rebreak.org
  (app.rebreak.org serves wrong TLS cert)
- native MagicSheet: fallback download/dmg URLs point to staging
- native settings: Magic sheet capped at detents=[0.85] so AppHeader
  stays visible
- bundles all in-flight Magic feature work (pair create/redeem,
  device endpoints, schema, adguard utils, mac-app, locales)
2026-06-03 08:25:02 +02:00

6.2 KiB

RebreakMagic Device-Binding — API Documentation

Backend-Implementation für DNS-basiertes Device-Binding via AdGuard Home DoH.

Architektur-Überblick

RebreakMagic.app (Swift/macOS)
    ↓ POST /api/magic/register (JWT Auth)
    ↓
Backend (Nitro)
    ├─ DB: UserDevice (magicDnsToken, magicEnrolledAt, ...)
    ├─ AdGuard REST API: Create Persistent Client
    └─ Response: { dnsToken, profileUrl }
    ↓
RebreakMagic.app → GET /api/magic/profile.mobileconfig?token=<dnsToken>
    ↓
macOS Configuration Profile (.mobileconfig)
    ↓ DNS-over-HTTPS: https://dns.rebreak.org/dns-query/{dnsToken}
    ↓
AdGuard Home (Hetzner) — Filtering + Logging per Client-ID

Endpoints

1. POST /api/magic/register

Registriert Mac als Magic-Client, generiert DNS-Token, provisioniert AdGuard.

Auth: Authorization: Bearer <jwt>

Body:

{
  "deviceId": "550e8400-e29b-41d4-a716-446655440000",
  "hostname": "Chahines MacBook Pro",
  "model": "MacBookPro18,3",
  "osVersion": "14.5"
}

Response (Success):

{
  "success": true,
  "data": {
    "deviceId": "550e8400-e29b-41d4-a716-446655440000",
    "dnsToken": "QX7g9kL2mN4pR6tV8wY0zB3cD5fG7hJ9kM2nP4qS6uW8xZ0",
    "profileUrl": "/api/magic/profile.mobileconfig?token=QX7g9kL2mN4pR6tV8wY0zB3cD5fG7hJ9kM2nP4qS6uW8xZ0",
    "existing": false
  }
}

Response (Limit erreicht):

{
  "statusCode": 409,
  "message": "Magic-Device-Limit erreicht (max 3)",
  "data": {
    "code": "limit_reached",
    "activeBindings": [
      {
        "deviceId": "...",
        "hostname": "Mac #1",
        "model": "MacBookPro18,3",
        "osVersion": "14.5",
        "magicEnrolledAt": "2026-06-01T10:00:00.000Z",
        "releaseRequestedAt": null
      }
    ]
  }
}

cURL:

curl -X POST https://staging.rebreak.org/api/magic/register \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "deviceId": "550e8400-e29b-41d4-a716-446655440000",
    "hostname": "Chahines MacBook Pro",
    "model": "MacBookPro18,3",
    "osVersion": "14.5"
  }'

2. GET /api/magic/devices

Listet alle aktiven Magic-Bindings des Users.

Auth: Authorization: Bearer <jwt>

Response:

{
  "success": true,
  "data": [
    {
      "deviceId": "550e8400-e29b-41d4-a716-446655440000",
      "hostname": "Chahines MacBook Pro",
      "model": "MacBookPro18,3",
      "osVersion": "14.5",
      "magicEnrolledAt": "2026-06-01T10:00:00.000Z",
      "releaseRequestedAt": null,
      "releaseAvailableAt": null
    }
  ]
}

cURL:

curl https://staging.rebreak.org/api/magic/devices \
  -H "Authorization: Bearer $JWT_TOKEN"

3. POST /api/magic/devices/:deviceId/request-release

Startet 24h Cooldown für Device-Freigabe.

Auth: Authorization: Bearer <jwt>

Response:

{
  "success": true,
  "data": {
    "releaseRequestedAt": "2026-06-01T10:00:00.000Z",
    "releaseAvailableAt": "2026-06-02T10:00:00.000Z"
  }
}

cURL:

curl -X POST https://staging.rebreak.org/api/magic/devices/550e8400-e29b-41d4-a716-446655440000/request-release \
  -H "Authorization: Bearer $JWT_TOKEN"

4. POST /api/magic/devices/:deviceId/cancel-release

Zieht Release-Request zurück.

Auth: Authorization: Bearer <jwt>

Response:

{
  "success": true,
  "data": { "ok": true }
}

cURL:

curl -X POST https://staging.rebreak.org/api/magic/devices/550e8400-e29b-41d4-a716-446655440000/cancel-release \
  -H "Authorization: Bearer $JWT_TOKEN"

5. GET /api/magic/profile.mobileconfig?token=<dnsToken>

Generiert personalisiertes macOS Configuration Profile.

Auth: KEINE (Token in Query-Parameter)

Response-Headers:

  • Content-Type: application/x-apple-aspen-config
  • Content-Disposition: attachment; filename="RebreakMagic-<deviceId>.mobileconfig"

Response-Body: XML-Plist (mobileconfig)

cURL:

curl "https://staging.rebreak.org/api/magic/profile.mobileconfig?token=QX7g9kL2mN4pR6tV8wY0zB3cD5fG7hJ9kM2nP4qS6uW8xZ0" \
  -o RebreakMagic.mobileconfig

DB-Schema

UserDevice Model (Prisma Schema):

model UserDevice {
  // ... existing fields ...

  // RebreakMagic DNS-Device-Binding
  magicDnsToken       String?   @unique @map("magic_dns_token")
  magicEnrolledAt     DateTime? @map("magic_enrolled_at")
  magicRevokedAt      DateTime? @map("magic_revoked_at")
  magicHostname       String?   @map("magic_hostname")
}

Migration:

# User führt aus (NICHT auto-deployen):
pnpm prisma migrate dev --name magic_binding_fields

AdGuard-Integration

API-Endpoint: https://dns.rebreak.org/control/clients/add

Auth: Basic Auth (ADGUARD_USER, ADGUARD_PASSWORD)

Payload:

{
  "name": "magic_<deviceId>",
  "ids": ["<dnsToken>"],
  "use_global_settings": false,
  "filtering_enabled": true,
  "parental_enabled": false,
  "safebrowsing_enabled": true,
  "blocked_services": []
}

DoH-URL-Format (embedded in mobileconfig):

https://dns.rebreak.org/dns-query/<dnsToken>

Cron-Worker

Funktion: processMagicReleases() in server/utils/magicCron.ts

Logic:

  1. Findet alle UserDevice mit releaseRequestedAt < NOW() - 24h AND magicRevokedAt IS NULL
  2. Für jedes Device:
    • DELETE AdGuard Client (/control/clients/delete)
    • Setze magicRevokedAt = NOW()
  3. Return { processed, errors }

Deployment: TODO — Nitro Scheduled Task oder externer Cron-Trigger


ENV-Variablen

Siehe ENV_VARS.md:

  • ADGUARD_BASE_URL — Default: https://dns.rebreak.org
  • ADGUARD_USER — Admin-User für AdGuard Home REST API
  • ADGUARD_PASSWORD — Admin-Password

TODOs (Phase 2)

  • Profile-Signierung via Apple Developer Certificate (/usr/bin/security cms -S)
  • Cron-Registration für processMagicReleases() (Nitro scheduled task oder externer Cron)
  • Plan-basierte Limits (jetzt hardcoded MAGIC_DEVICE_LIMIT = 3)
  • AdGuard Blocked-Services konfigurieren (Gambling-Filter via AdGuard-Blocklisten)
  • Tests (Phase 2: rebreak-tester)
  • Frontend-Integration (RN-UI + RebreakMagic.app)