> 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

Neither mode is unbreakable, and the limit is different in each.

**License check.** Your jar is on the customer's disk, so someone who edits it can delete the check. What it reliably stops is license sharing, expired use, and refunded copies that keep running.

**Protected plugin.** There is no jar on disk, but the JVM still belongs to the attacker: a determined party can attach a Java agent or dump heap memory and recover the decrypted bytes at runtime.

Set expectations accordingly. The durable value in both cases is:

1. **Signed answers.** Every reply is Ed25519-signed over a nonce your plugin chose, so a fake server or a replayed response cannot grant a license.
2. **No plaintext jar on disk** (protected mode), which defeats casual decompilation and redistribution.
3. A server-side **kill switch**, cutting off any copy on its next check.
4. **Per-machine binding**, so one key cannot silently run everywhere.
5. Full **access logging** (IP, OS, machine id) to 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.
