> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arcane-powered.com/sdk/llms.txt
> Use this file to discover all available pages before exploring further.

# Ownership model

> Default launch ownership check via arcane_init, and when DRM is skipped.

By default, **`arcane_init` is the ownership check**. Call it once at launch with your portal public key. You do not add a second ownership step unless you need a custom flow.

The game does not talk to the network for this: the Arcane desktop launcher refreshes tickets online; the SDK only reads and verifies the local cache.

## Portal public key

The string you pass to init is the **public key** generated for your title in the Arcane portal. In the current crate API the parameter is still named `game_id` — pass the portal public key there. The same value keys local flag and ticket files, and must match the ticket `gid` claim.

## Default init policy

`arcane_init(public_key)`:

1. Reads `flags/{public_key}.json` for `drm_enabled`
2. If `drm_enabled` is `false` → returns `OwnershipStatus::DrmDisabled` (no ticket required)
3. Otherwise (flag `true` or missing) → runs the offline ownership check

That is the whole default launch path.

## Offline check (what init runs when DRM is on)

When DRM is not disabled, init verifies the cached ticket:

1. Loads a ticket file for the public key (scans `tickets/{user_id}/`)
2. Rejects clock rollback vs `last_seen_wall_time`
3. If `drm_enabled` on the ticket file is false → `DrmDisabled`
4. Ensures the ticket string is non-empty
5. Compares cached `device_hash` to the local fingerprint
6. Verifies the JWT (ES256, issuer `arcane-drm`, audience `arcane-game-sdk`)

## Data layout

```
{app_data}/Arcane Powered/drm/
├── machine_id
├── jwks.json
├── flags/{public_key}.json
└── tickets/{user_id}/{public_key}.ticket
```

`drm_data_root()` resolves `{app_data}` via the OS application data directory.

## Ticket claims

| Claim                 | Role                                                |
| --------------------- | --------------------------------------------------- |
| `gid`                 | Must match the portal public key you passed to init |
| `own`                 | Must be `true`                                      |
| `dev`                 | Must match local `device_hash`                      |
| `iat` / `nbf` / `exp` | Time bounds (±5 min skew)                           |
| `sub` / `jti` / `ver` | Subject, ticket id, version                         |

## Device fingerprint

`machine_id` is a UUID written once under the DRM root (mode `0600` on Unix). `device_hash` is the first 16 bytes of SHA-256(`machine_id`), hex-encoded. Tickets are bound to that hash so they cannot be copied to another machine.

## JWKS

Verification keys come from `jwks.json`. The JWT `kid` selects the key when present. If JWKS is missing, the SDK returns `ticket_invalid` — refresh via the desktop app.

## Custom check only

`check_ownership_offline` always runs the ticket path (useful if you manage DRM policy yourself). Prefer `arcane_init` for normal launch.
