> For the complete documentation index, see [llms.txt](https://docs.ohalee.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ohalee.com/products/obsidian-license/security.md).

# Security Model

## An honest note

Obsidian License **significantly raises the bar but is not unbreakable**. The JVM belongs to the attacker: a determined party can attach a Java agent or dump heap memory and recover the decrypted bytes at runtime. It's important to set expectations accordingly.

The durable value is:

1. **No plaintext jar on disk** — defeats casual decompilation and redistribution.
2. A server-side **kill switch / revocation** — cut off any copy on its next boot.
3. **Per-machine activation binding** — one key can't silently run everywhere.
4. Full **access logging** (IP, OS, hardware id) — detect and investigate abuse.

Obfuscation (e.g. ProGuard) of the protected jar and anti-agent hardening are complementary follow-ups, intentionally out of scope for the first version.

## What protects a copy in the wild

| Layer           | Mechanism                                                                                                                                                                 |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| At rest         | Protected jars are encrypted **AES-256-GCM**; the plaintext is discarded on upload.                                                                                       |
| In transit      | Activation responses are **Ed25519-signed** (`nonce.version.payload.key.ent`), so a rogue server can't substitute a payload and tier/entitlements can't be tampered with. |
| On the customer | The loader decrypts and class-loads **in memory** — nothing touches disk.                                                                                                 |
| Binding         | Each license is bound to a configurable number of machine fingerprints.                                                                                                   |
| Revocation      | Revoke / expire / rebind from the dashboard; the plugin stops loading on affected servers at next boot.                                                                   |
| Abuse           | Per-IP auto-ban and per-license rate-limiting blunt sharing and brute force.                                                                                              |

## Your account security

On the hosted service, the encryption keys and infrastructure are managed for you. What you control on your account:

* **Account security** — TOTP-based **2FA** on login, plus an **audit log** of sensitive actions (revocations, plan changes, etc.).
* **API keys** — personal, revocable tokens for automating license management or CI-driven uploads; revoke any that leak.
* **GDPR** — self-service account data export and account deletion are built in.

{% hint style="info" %}
If you self-host, you additionally own the **Ed25519 signing keys** and the `MASTER_KEY` that wraps per-product AES keys at rest — back both up, because losing `MASTER_KEY` makes stored product keys unrecoverable. On the hosted service this is handled for you.
{% endhint %}

## Fail-closed behaviour

The loader is strict-online and fails closed: if the config is missing/invalid or the server is unreachable, the protected plugin does **not** run. There is no offline grace mode by default.
