Security
The threat model for Plinth, what Seal does and does not protect against, the wallet permission model, and the deployment trust model.
Threat model
| Adversary | Goal | Mitigation |
|---|---|---|
| Public observer | Read sensitive fields of a submission | Seal field level encryption, key release gated to allowlist wallets |
| Form owner gone rogue | Edit or fabricate a submission | Submission objects are owned by the submitter, the form owner has no write authority |
| Form vendor (Plinth itself) | Hold data hostage | All blobs live on Walrus mainnet, the Move package is immutable except via the upgrade cap held by the owner address, frontends are interchangeable |
| Walrus storage node | Tamper with blob bytes | Walrus erasure coding plus blob ID hash check on every read |
| Seal key server | Leak data key without policy approval | Threshold key sharing across multiple key servers, policy enforced on every session key request |
| Compromised admin wallet | Mass exfiltrate sensitive fields | Owner can rotate the admin allowlist via AdminCap, the affected admin wallet can be removed in one transaction |
What Seal protects
- Confidentiality of fields marked
encrypted: trueagainst any party not on the form admin allowlist. - Confidentiality of those fields against the Plinth frontend, the Walrus storage layer, and any third party indexer.
What Seal does not protect
- Confidentiality of fields marked
encrypted: false. These are written in cleartext to a Walrus blob and are publicly readable by anyone who knows the form ID. - Confidentiality of metadata. The submission existence, the submitter address, the submission timestamp, and the form ID are visible onchain by design.
- Compromise of an admin wallet private key. If an admin wallet is exfiltrated, every submission that wallet can decrypt is exposed until the wallet is removed from the allowlist.
Wallet permission model
Plinth uses three capabilities expressed as Sui objects.
OwnerCap
Held by the form owner. Confers the right to:
- Retire a form.
- Transfer ownership to a different wallet.
- Update the schema blob ID or schema version.
The OwnerCap is the highest privilege. It should be stored in the safest wallet available to the form owner. For mainnet deployments of Plinth itself, the OwnerCap for the registry is transferred to the operator owner address at the end of the deploy script.
AdminCap
Held by every wallet on the admin allowlist. Confers the right to:
- Decrypt sensitive fields via Seal.
- Annotate submissions in the index with notes, priority, and status.
AdminCap does not confer the right to delete or edit submission blobs. It is read plus annotate, never destructive.
UpgradeCap
Sui standard package upgrade capability. Held by the operator owner address. A package upgrade is a Sui transaction signed by the holder. This is the only path to change Plinth Move logic after publish.
Deployment trust model
Plinth ships with a three wallet deploy pattern.
Wallet A. Operator owner
Held entirely by the operator. Receives OwnerCap, AdminCap for the registry, UpgradeCap, and any leftover SUI plus WAL after the deploy script cleanup transfers run.
Wallet B. Deployer
A dedicated wallet provisioned for the publish flow. Funded with a small amount of SUI for gas and a small amount of WAL for initial blob storage. The deployer wallet private key is supplied to the deploy script through an environment variable, used once to publish the package and write seed blobs, then idled. After cleanup, the deployer wallet holds approximately zero SUI and zero WAL and zero capabilities. The deployer key can be rotated or deleted at the operator discretion.
Wallet C. Prize recipient
Optional. If the operator participates in a Walrus program that pays out in WAL, this is the wallet registered as the recipient. Wallet C may be the same as Wallet A.
The deploy script is intentional about this trust model. It runs in this order:
1. Verify deployer wallet has sufficient SUI and WAL. 2. Publish the Move package. 3. Capture the package ID and the upgrade cap object ID. 4. Initialize the form registry. 5. Write the seed schema blob to Walrus. 6. Transfer the upgrade cap to the operator owner. 7. Transfer the form registry owner cap to the operator owner. 8. Sweep remaining SUI and WAL from the deployer to the operator owner. 9. Print all object IDs and transaction digests.
After step 9, the deployer wallet has no continuing authority. Auditing the resulting object graph on Sui mainnet shows that Wallet A is the rightful owner of every privileged object.
Disclosure
To report a vulnerability, contact the maintainer through the email listed in the repository profile. Do not open a public issue for security findings. Standard responsible disclosure applies.
For the data model and how blobs are laid out, see the Architecture page.