Every plugin starts with plugin.toml and this schema:
schema = "pentect.plugin.v1"
name = "my-plugin"
description = "What the plugin protects."Unknown fields cause an error. This helps catch spelling mistakes.
Basic fields
| Field | Required | Meaning |
|---|---|---|
schema | Yes | Must be pentect.plugin.v1 |
name | Recommended | Lowercase plugin name shown by the CLI |
description | Recommended | Short text shown before approval |
binary | For Wasm | Release filename ending in .wasm |
repository | For released Wasm | GitHub repository in OWNER/REPO form |
required | No | Stop when the plugin cannot run; default is false |
postscript is not supported. A plugin cannot run an installer or native setup program.
What users approve
Before activation, Pentect shows the plugin identity, hooks, required status, execution limits, network origins, HTTP methods, and private or insecure network access. The approval is tied to the manifest and verified Wasm binary.
| Change | Result |
|---|---|
| Regex, hook, access, or manifest changes | Review and approval are required again |
| New verified binary with the same approved access | Update can continue without wider permission |
| Checksum or GitHub build record does not match | Installation stops |
| Required plugin fails at runtime | The protected action stops |
| Optional plugin fails at runtime | Pentect reports partial plugin coverage and continues |
--yes accepts the displayed approval without another prompt. It never turns off validation, checksums, build-record verification, or sandbox limits.
Regex detectors
Add one or more detector tables:
[[detector]]
label = "CUSTOM_TOKEN"
pattern = '''\bct_[A-Za-z0-9]{24}\b'''
category = "secret"
confidence = "high"
prefilter = ["ct_"]
capture = 0| Field | Default | Meaning |
|---|---|---|
pattern | — | Rust regular expression; required in plugin.toml |
label | CUSTOM | Label used in the handle |
category | secret | secret, identifier, endpoint, pii, or other |
confidence | high | high, medium, or low |
capture | 0 | Regex capture group to protect; 0 means the full match |
prefilter | none | Plain text that must exist before the regex runs |
validator | none | luhn, iban_mod97, verhoeff, or another built-in validator |
Inline regex plugins can add checks. They cannot disable built-in Pentect checks.
Wasm binary and publisher
binary = "my-plugin.wasm"
repository = "owner/my-plugin"
[publisher]
workflow = ".github/workflows/release.yml"Pentect downloads the binary and its .sha256 file from the latest GitHub Release. It also checks the GitHub build record against publisher.workflow. The workflow path must stay inside the repository.
Most plugins use the binary name as the release asset. An older manifest can override the portable asset name with assets.wasm32, but new plugins should use the same name in both places.
Execution limits
[execution]
runtime = "wasm"
mode = "oneshot"
timeout_ms = 10000
max_input_bytes = 262144
max_output_bytes = 1048576
max_spans = 512Only wasm and oneshot are supported. The largest allowed values are 60 seconds, 4 MiB input, 4 MiB output, and 4,096 findings. The Wasm file itself must be 32 MiB or smaller.
Use small limits. They protect the user from a slow or broken plugin.
Pentect also applies one shared ceiling to the complete plugin chain for a protected action: 60 seconds, 16 MiB total input, 16 MiB total output, 8,192 findings, and 32 brokered HTTP requests. These host limits need no manifest settings. A plugin's own limits can only make its invocation stricter.
Wasm modules are checked before compilation with wasmi's strict untrusted module limits. At runtime Pentect permits one memory, one table, and one instance, caps memory at 64 MiB and table size at 4,096 elements, and uses a fuel budget for Wasm instructions. Memory or table growth beyond those host caps traps the plugin. The chain deadline is also used by brokered HTTP calls. Fuel bounds compute work; it is not a general thread preemption mechanism for host code.
Network access
Do not add this table when the plugin needs no network:
[network]
allow = ["https://policy.example.com"]
methods = ["POST"]
max_request_bytes = 262144
max_response_bytes = 1048576
max_requests = 4Each item in allow must be an exact origin. It can contain a scheme, host, and port, but no path, query, credentials, or fragment. A plugin cannot use a different origin at runtime.
Local services need clear extra access:
[network]
allow = ["http://127.0.0.1:8787"]
methods = ["POST"]
private_network = true
allow_insecure = trueNetwork limits cannot be larger than 64 origins, 16 requests per hook, 1 MiB per request, or 4 MiB per response. Pentect also blocks unsafe address changes during DNS lookup.
Full Wasm example
schema = "pentect.plugin.v1"
name = "company-policy"
description = "Apply our local text policy."
binary = "company-policy.wasm"
repository = "example/company-policy"
required = true
[publisher]
workflow = ".github/workflows/release.yml"
[execution]
runtime = "wasm"
mode = "oneshot"
timeout_ms = 5000
max_input_bytes = 262144
max_output_bytes = 262144
max_spans = 256
