P6M Dashboard

P6M knowledge base

P6M Knowledge Base

Search P6M docs with citations and Memory Graph context.

Results are pulled from the P6M knowledge domain and include citation metadata.

Source document

13. Policy WASM modules

Back to sources
active text/markdown v1 24 Jul 2026, 04:45

p6m://docs/130-policy-wasm-modules

Policy WASM modules

Policy modules let an Org attach custom logic to P6M domain events. They are useful when a customer wants P6M to enforce rules before data is accepted, without asking P6M operators to hard-code customer-specific behavior.

The supported events are http.before_request, http.after_response, and metadata.before_write.

Policies are per enabled service. The active binding is:

terminal
enabledServiceId + event

That means a policy for the metadata enabled service does not run for knowledge, support, notifications, or any other domain. To enforce the same rule across multiple domains, upload and activate a module for each enabled service.

http.before_request runs before a customer API handler for an enabled service. It can reject a request or apply a narrow allowlisted mutation to the JSON body or safe customer headers.

http.after_response runs after a successful customer API handler response. It can enrich the JSON response or add safe customer headers before the response is returned.

metadata.before_write is the metadata-specific pre-commit hook. If the active policy denies the request, P6M returns a forbidden response and the metadata write does not happen.

What policies are for

Use policy modules for rules such as:

  • reject metadata keys that are not on an allowlist
  • block writes to production unless the actor has a specific scope
  • prevent customer apps from storing sensitive values in metadata
  • require resource IDs or tags to follow a naming convention
  • enforce tenant-specific rules at the edge of a domain

Do not use policies for long-running workflows, network calls, or background jobs. Policies should be deterministic, fast, and local.

Lifecycle

A policy module moves through a simple lifecycle:

  1. Upload a draft module.
  2. Validate the module hash and metadata.
  3. Activate the module.
  4. P6M demotes any previous active sibling for the same Org, enabled service, and event.
  5. Disable or delete old modules when they are no longer needed.

Only one module should be active for the same domain installation and event.

Upload a module

The API expects a base64-encoded WASM module and, optionally, the expected SHA256 of the decoded module.

terminal
curl -X POST "$P6M_URL/v1/logic-modules" \
  -H "Authorization: Bearer $P6M_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "orgId": "'"$ORG_ID"'",
    "enabledServiceId": "'"$ENABLED_SERVICE_ID"'",
    "name": "Require tenant header",
    "event": "http.before_request",
    "wasmBase64": "'"$WASM_BASE64"'",
    "expectedSha256": "'"$WASM_SHA256"'"
  }'

The internal dashboard also has a Policies page that can upload a policy module for an Org. The dashboard upload path is intended for operators and early testing. Customer app automation should use the API.

Activate a module

After upload, activate the module:

terminal
curl -X POST "$P6M_URL/v1/logic-modules/$MODULE_ID/activate" \
  -H "Authorization: Bearer $P6M_API_KEY"

Activation demotes the previous active module for the same Org, enabled service, and event. This gives you a clean replace-in-place update flow.

Update a module

Policies are immutable after upload. To update a policy:

  1. Build a new WASM module.
  2. Compute its SHA256.
  3. Upload it as a new draft.
  4. Activate the new module.
  5. Test the guarded domain behavior.
  6. Disable or delete the old module when you are comfortable.

Keeping old modules around briefly gives you an audit trail and a manual rollback target.

Disable a module

Disable a module when you want the guarded domain event to proceed without that policy:

terminal
curl -X POST "$P6M_URL/v1/logic-modules/$MODULE_ID/disable" \
  -H "Authorization: Bearer $P6M_API_KEY"

Runtime behavior

Policy evaluation is fail closed. If an active module exists and the runner errors, P6M denies the guarded operation. This is safer than accepting writes after a policy failure.

Denied requests return forbidden. For metadata.before_write, the business transaction is rolled back. Usage metering for the denied attempt is recorded separately so billing and audit can still reflect the attempt.

If a policy tries to mutate something outside the allowlist, P6M rejects the request and writes an audit event:

terminal
logic_module.mutation_rejected

The audit row targets the enabled service and includes the Org, workspace, enabledServiceId, event, and rejection reason. The module cannot suppress or alter this audit record.

Safe mutation boundary

Allowed before-request mutations:

  • requestBodyMergePatch
  • requestQuerySet
  • requestHeadersSet

Allowed after-response mutations:

  • responseBodyMergePatch
  • responseHeadersSet

P6M rejects attempts to mutate immutable identity, authorization, billing, usage, credential, forwarding, or P6M internal header fields. In practical terms, policies cannot change orgId, workspaceId, enabledServiceId, actor identity, scopes, Authorization, cookies, Host, Content-Length, X-Forwarded-* headers, or X-P6M-* headers.

Current limitations

  • Modules can only use supported event names.
  • Modules are capped in size.
  • Upload validates SHA256 when expectedSha256 is provided.
  • External network calls from policy code are not part of the policy contract.
  • A policy should return quickly; use app workflows or notifications for slower decisions.

Testing policy behavior

Use a staging workspace first:

  1. Enable metadata for the staging workspace.
  2. Upload and activate the policy module for http.before_request.
  3. Try a customer API request that should be allowed.
  4. Try a customer API request that should be denied.
  5. Confirm denied requests return forbidden.
  6. Confirm audit and usage show the policy-denied attempt.

After that, repeat the same rollout for production.

What is charged

Policy modules are charged for the extra work P6M performs while enforcing customer-defined logic:

  • policy module uploads
  • policy activation/update lifecycle operations
  • policy evaluations on guarded domain events
  • policy-denied attempts where P6M evaluated the module and rejected the operation
  • policy module storage
  • request compute used while processing guarded operations

For metadata.before_write, the metadata write itself is rolled back if denied, but the policy evaluation and denied attempt can still be metered because P6M performed work for the customer.