IP Vault
Protect the assets you ship.
IP Vault seals the models, data and content your app ships, in your own CI, and issues the key that opens them once to each licensed installation, wrapped to a key in the device’s hardware key store. A copy of your package is no longer a copy of your product.
The product
IP Vault
Some assets have to be on the device to be worth anything — a model that runs offline, a content library, an engine that has to answer in milliseconds. IP Vault ships those assets without also shipping the ability to open them. The assets travel sealed. Each installation receives the key that opens them once, from the vault service, wrapped to a key in its device’s hardware key store that cannot be exported. It never passes through your code, and nothing in the package can open them. It puts the cost of copying back where it was before AI agents made taking a protected build apart cheap (what changed).
More than encryption at rest
Encrypting a file is the easy part. Encryption at rest that ships inside an app ships its key with it, in the code, in a resource, or worked out from both, so whoever has the package can find the key, and one find opens every copy. Sealing is where IP Vault starts. What it adds is everything that decides who gets a key, where the key can live, and for how long.
| What it adds | What that means |
|---|---|
| No key in the package | Nothing in the app, its sealed files or your code can open them. A copied package, or a copy of an installed app’s data, carries nothing that does. |
| A key only for an install you licensed | The vault service checks the device’s hardware attestation: a genuine device with its bootloader locked and verified boot, running your app signed with your key. Play Integrity too, where you require it. Then your licence: an activation code with a device limit, or a voucher your own backend signs. |
| Bound to that device | The key arrives wrapped to a hardware key that cannot be exported, so the stored copy is useless on any other device. |
| Opened in memory, one use at a time | Nothing opened is left on disk. With the ONNX Runtime and LiteRT adapters your app gets results and never holds the model. On Android devices that support it, the asset key itself can stay in the hardware key store and be used there; the data it opens is still in memory while in use. |
| Licences that end | Each installation holds a lease: it works offline until the lease and a grace period run out, and renews when it can. Revoke an installation and it cannot renew. |
| Keys that rotate | Every release seals with fresh keys. A key taken from one version opens that version, not the next. |
| Tampering is refused | A sealed file that was modified does not open. |
| Proof from your pipeline | The plaintext never leaves your runner. A check after the build fails it if any plaintext input shipped, whole or in part, and every release, activation and revocation goes into a hash-chained audit log. |
How it fits together
Three vault components, plus the application that integrates them. The integration boundary is the point: the key never crosses it.
| Component | Runs in | Role |
|---|---|---|
| Vault packer | your CI: GitHub Actions or GitLab CI/CD | Seals the assets your manifest names, on your own runner, and registers their keys with the vault service. The plaintext never leaves the runner. A check after the build fails it if any plaintext input shipped. |
| Vault SDK | the application bundle | Registers the installation once, receives the key, places it in the platform key store, and opens a sealed asset in memory when the application asks for one. |
| Vault service | self-hosted or managed | Issues the key to a registered installation — once — and can refuse, revoke or rotate it later. |
| The application | the integrating codebase | Calls the SDK and uses what comes back. It handles no key, carries no decryption code, and needs no network once the installation is registered. |
The key moves once
| When | What happens |
|---|---|
| Once, at first run | The SDK registers the installation with the vault service over an authenticated channel, proving it runs on genuine hardware, and receives the asset key wrapped to a key held in the device’s hardware key store — the Android Keystore today, with the Secure Enclave on Apple devices in development — which cannot be exported. Only that wrapped copy is stored, it is useless on any other device, and it never passes through your code. |
| Every use after that | Your app asks the SDK for an asset. The SDK uses the key already in the key store, opens the asset in memory for that one operation, and returns it. No network, no per-use round trip, and nothing opened is left on disk. |
| When something changes | The vault service can refuse to register an installation, revoke a key it already issued, or rotate to a new one with your next release. A key never leaves the device it was issued to. |
What this does not do
It does not make copying impossible, and we would not sell it as if it did. A device that is allowed to open an asset can in principle be made to give it up, and a key in a hardware key store is still a key on a device.
What it does is put the effort back. There is no key in the package and none that can be worked out from it, so the cheap attack — the one that runs offline against a free download and opens every copy at once — stops existing. A phone rooted the usual way cannot hold a key either: on Android, unlocking a phone to root it wipes the key along with everything else, and current iPhones have no public jailbreak at all. What remains is breaking into a locked, up-to-date device that holds a key, and instrumenting the application while it has an asset open.
That is rare skill and real time — the two things agents took away. It is the bar your protection was bought against in the first place, put back. The claim is not that nobody can do it. It is that doing it costs what it used to, that it can only be done on a device that registered for a key, and that the key is revoked and rotated out with your next release.
Fits well
Models, weights, datasets, maps, content libraries and licensed engines — anything whose value survives being copied and that has to be on the device to do its job. Products that can expect a network connection once, at first run. Android today, with adapters for ONNX Runtime and LiteRT and plain data for anything else; iOS is in development.
Needs design work first
Products that must work fully before any registration has happened. Desktop and web builds: IP Vault is for mobile devices, where a hardware key store is standard. Assets that have to be handed to someone else’s runtime in the clear. Unsealing inside a loop where microseconds matter.
Who it’s for
Who needs it.
You license an engine, a model or an SDK to other companies
Each licensee should run what it paid for, on the devices it paid for. Today a licensee who has the package has the engine. With IP Vault, a licensee has an activation code or your backend’s voucher, and the asset opens only on the installations it covers.
Your app runs a model on the device, and the model is the product
It has to be on the phone to work offline and answer fast, and it is the thing a competitor would copy. It ships sealed and opens in memory for the session that uses it.
You ship paid content, maps, datasets or rules inside the app
A licence has a lease. When a subscription ends and you revoke its licence, the installation can no longer renew, and the content stops opening once the lease and its offline grace run out, instead of staying as a permanent copy.
You already encrypt your assets, and the key ships with them
That is the most common way encryption fails. IP Vault keeps the key out of the package entirely: it is issued per installation, by a server you control, into hardware.
And the honest other side: if your paid value is produced on your own servers after checking the subscription, and nothing valuable ships to the device, you do not need IP Vault. Keep it on the server.
Start free
Put it in your pipeline.
IP Vault runs in the CI you already have, GitHub Actions or GitLab CI/CD, and starts on a permanent free tier.
| Step | What happens |
|---|---|
| 1 Declare your assets | One file, ipvault.toml, lists each asset, where its plaintext lives (outside anything you package) and where the sealed file goes. |
| 2 Seal and register in CI | The GitHub Action, or the same CLI in a GitLab job, seals each asset on your own runner and registers its key with the vault service. The plaintext never leaves the runner, and the key never touches disk. |
| 3 Build and check | Your normal build packages the sealed files. A check after the build fails it if a sealed file is missing or any plaintext input shipped, whole or in part. |
| 4 Open them in your app | The SDK activates each installation once and opens assets in memory, offline. Activation codes, or vouchers from your own backend, decide who gets a licence. |
Three identities, kept apart
None of them is ever embedded in your app. A CI token cannot read a key, change a version you already shipped, or touch another app, and a pull request from a fork never receives one.
Self-serve signup is not open yet. The free tier opens with it, and this page will link to it then — it would rather say so than collect addresses for something that is not ready. To integrate before then, ask us about a design-partner place: talk to us.
No account yet? The free scan checks a release you already ship for models in plaintext. It needs no account and runs in the same two CI systems; the integration guide has the one step it takes.
What changed
What used to need a specialist now needs an agent.
Taking a protected build apart used to need two things that were hard to come by together: rare skill, and weeks of patience. AI agents have lowered both bars at once. Less expertise to begin, far less time to finish, and the work no longer has to be done by someone who has done it before.
| Before agents | With agents |
|---|---|
| Finding the one line that decides access, inside code built to be unreadable, was days of expert work. | It is a search through machine-read output, and the expert is optional. |
| Rebuilding the state a protection layer needs before the app will start was a custom job every time. | That state can be listed and rebuilt — and what the protection needs is often recoverable from files the protection itself ships. |
| Working around a protected piece of native code meant matching a hidden interface by hand. | Where the protected piece is a standard runtime, the official public build drops straight in — and the product’s own code then loads and runs untouched. |
Your protection did not get weaker. The attacker got cheaper, and got there sooner. A stack chosen to stop a rare specialist with weeks to spare is not the same stack once it is measured against an agent that works the same problem in an afternoon. What was sufficient then is not automatically sufficient now — and almost nobody has re-measured.
So the job is not to make your product impossible to copy. Nothing is impossible to copy. The job is to put the cost back where your protection was priced: a specialist, and weeks.
What we find
How protection gets bypassed.
We have taken this work all the way through on real, shipping products: paid value obtained on an ordinary device, with no purchase, still working after a restart. Below are the ways it happens. We describe them as patterns rather than case studies because the details belong to the companies involved — we tell them first, in private, and we do not publish their names.
One flag inside the app, and your engine runs for free
A paid app works out "has this person paid?" inside itself. Everything after that trusts the answer — the paywall, the menus, and the part that actually produces the paid result. Protection makes that answer expensive to reach. But nothing asks again at the moment the paid thing is handed over.
Change the answer once and the product runs its own engine, unchanged, on a phone with no purchase on it. The result stays after a restart, because by then it is just data the app saved for its owner.
How to check your own buildFollow the value that unlocks a paid feature all the way to the code that produces the paid result. Ask whether anything in between talks to a server that could say no.
The lock ships with its key
Models, weights, media and settings are shipped encrypted. That is the right instinct and more than most products do. The problem is one level down: the key can be worked out from the package it arrives in. Sometimes it is simply stored there. More often it is calculated from something inside the same download, by code that takes no input from outside the file.
No licence, account, token, server answer or device check takes part. When that is true, the lock buys time instead of drawing a line — and it opens offline, for anyone who downloads the product. Including the people who already pay, who do not need their licence to read it.
How to check your own buildAsk what the decryption step takes as input. If every input is inside the file you ship, the key is inside the file you ship.
A free account asks for the paid content, and gets it
Here the server-side checks are real, and in places exactly right. They are applied feature by feature. But the layer that hands over the actual thing — the catalogue, the media, the files — checks nothing. An account that has never paid asks for it, and gets it.
The sharpest version of this is not a missing check. It is a working one that nobody reads: the server correctly refuses, says so in its answer, and the app never looks at that part of the answer.
How to check your own buildSign in with a free account and ask for the paid content directly. Look at what comes back, not at what the app chooses to show.
Your backend trusts whoever asks nicely
Keys, tokens or service credentials recoverable from the build let someone call the paid API directly. Your app is never involved, so none of the checks inside it apply, and neither do your rate limits or your per-account quotas. What you sell is reachable by anyone who read the package once.
How to check your own buildPull the strings and configuration out of your own release build and ask whether any of them would let a stranger call a paid endpoint without your app in the picture.
Your engine runs fine without your app
The native library or model loads and answers from a plain test harness. The interface was the only thing holding it, and an interface is not a licence. For anyone who licenses an engine or an SDK to other companies, this is not a bug in a feature — it is the business, running for whoever took a copy.
How to check your own buildAsk whether your engine can be called without your interface. If someone on your team could get an answer out of it with a twenty-line harness, so can anyone who downloads the product.
One month buys the library forever
Paid content downloads into a cache with no expiry and no lease. A single honest subscription becomes a permanent copy the day it is cancelled, and nothing on the device ever asks whether the person is still paying.
How to check your own buildSubscribe, download, cancel, then turn off the network. Whatever still works is what one month actually buys.
The lifetime licence is a line in a settings file
A one-off or perpetual tier is granted once on the device and never revalidated — and by definition it has to keep working forever, with no renewal event to re-check it against. The loss per bypass is also the whole lifetime price rather than one month of it.
How to check your own buildFind where a perpetual grant is stored on the device and ask what revalidates it. If the answer is nothing, that storage is the entire control.
The desktop or web build gives up what mobile protects
The same asset ships unhardened in a web bundle or a desktop package, because the protection budget went where the install numbers are. Nobody attacks the platform you hardened. They take the one you did not, and the asset is identical either way.
How to check your own buildOpen the same asset on every platform you ship it to, and judge yourself by the weakest one.
The protection wraps the app, not the asset
The binary is hardened and the model, media or dataset sits beside it in the clear. Hardening covers the code that reads the asset rather than the asset itself, so the thing you bought protection for is not the thing the protection covers.
How to check your own buildWrite down what your hardening actually covers, then write down your crown jewels. Compare the two lists.
No two of those fail in the same place: a decision made inside the app, a key handled badly, a permission check in the wrong layer, a credential left in the build, an engine that answers to anyone, an asset the hardening never covered. What they share is not a type of bug:
In every case, the thing that decided whether to hand over paid value could be reached by the person who benefits from the answer.
That is why this is not a check on your obfuscation. It is a check on a whole boundary — and it is why buying more protection from one vendor does not close it. Pattern 03 is the one worth sitting with: the check existed, worked, and gave the right answer, and it was thrown away one layer up. Nothing makes a better case for testing the whole path end to end.
If none of these is quite your product, that is not the all-clear it sounds like. Every shape on this page was one nobody had written down until someone looked at that particular product. The check does not start from a list of known shapes — it starts from the assets you name as valuable, and asks what it would take to obtain each one.
The service
IP Exposure Assessment
A fixed-scope engagement, in five steps, ending in a verdict you can act on rather than a list of warnings. The question it answers is what your product costs to copy today — and what it would cost after the changes we recommend. Scope and price are agreed before it starts, and you keep what it produces.
| Step | What happens | What you get |
|---|---|---|
| 1 You tell us what matters | You name the things you sell — the engine, the model, the content, the paid feature — and what would count as losing each one: copied out, run without permission, used offline, run under someone else’s signature, or shipped on as a working product. | One short, checkable statement per asset. This is what makes the test repeatable next release. |
| 2 We map how it could leave | We read your release files: what protection is there, where the paid/not-paid decision is made and who reads it, which code is yours and which is standard, where the keys are, and what still works offline. | A map from each asset to every route that hands it over, and which control is supposed to stop each one. |
| 3 We watch it run | We install your product the way a customer does and buy the subscription for real. Then we drive the paid flow and ask one question: at the moment the paid thing is handed over, is there a call that could refuse it? | If the answer is yes, you get a cheap, honest "no problem found here" in days instead of weeks. |
| 4 We try to take it | With your written permission we change your build in small, logged steps and run it on isolated machines against your test servers. | Every input, file hash, change, command, device and result written down. |
| 5 You get a verdict | One of: copied out, reused, shipped on, refused by your server, slowed down but not stopped, or protected — with the checks that prove it named. | The evidence, a fix list in priority order, and a re-test once you ship the fix. |
Protected
“Your server already refuses this” is a real result, not a failed job. It is what a release decision needs, and almost nobody will sell it to you. We will.
You keep the test
The check leaves written, re-runnable statements behind, so the second one is a quick re-test rather than buying the whole thing again.
Is this you?
Six questions.
And the honest other side: if your paid value is produced on your own servers after checking the subscription, and nothing valuable ships to the client, you are probably fine. We will tell you that in days rather than weeks.
Who we are
rev3rse
We are a security research firm. We work on the thing that actually ships — the built app, the running service, the file on the device — with a public research record, a published disclosure policy, and a method we write down. This practice is that method pointed at one question: can the thing you sell be taken out of the thing you ship it in?
Talk to us
Protect the assets you ship.
Tell us what you sell, and what you would least like to see leave the machine it ships on — for an assessment, or a design-partner place on IP Vault. If you would rather just write to us: hello@rev3rse.dev.
Not ready for the full assessment? Ask for the short read: we look at your current release without changing or running anything, and send back one page within five working days.