# 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:**

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

**Response (Success):**

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

**Response (Limit erreicht):**

```json
{
  "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:**

```bash
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:**

```json
{
  "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:**

```bash
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:**

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

**cURL:**

```bash
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:**

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

**cURL:**

```bash
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:**

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

---

## DB-Schema

**UserDevice Model (Prisma Schema):**

```prisma
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:**

```bash
# 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:**

```json
{
  "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](../ENV_VARS.md#rebreakmagic-dns-over-https-neu-2026-06-01):

- `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)
