The trust boundary

A campaign template is authored elsewhere and lands in your CRM. Identities and foreign ids are stripped, and a webhook step is refused outright, not sanitised.

Trust boundary

A pack is read as hostile by accident.

Not malicious – just full of ids that meant something where the pack was authored and mean something else, or nothing, in your workspace. So a package is parsed and proven safe before a single row is written, and the rules doing it are structural: three families of check, each one there because somebody worked out exactly what breaks without it.

How it works

The three families of rule

None of these is a heuristic. Each is a fixed rule with a named failure behind it.

Identities are stripped

Owner and creator fields decide the sender name on an email and who a task is assigned to. A pack carrying them would make your workspace send mail as the publisher, so they are removed rather than remapped.

Foreign ids are stripped

A tag id from another workspace points at your tag, somebody else’s, or nothing – and a goal wired to it never fires, so the author sees a branch that silently does not work. Names survive; ids do not.

Two step types are refused

A webhook step carries a publisher-controlled address that would receive the installing workspace’s contact data. There is no safe sanitised version of that, so the step type is refused and the install fails loudly. A create-deal step is refused for a quieter reason: it needs a pipeline stage that only exists in your workspace, and stage ids are on the strip list — so a packaged one could only ever arrive with no stage, which the runner answers with a log line and a step that does nothing. Refusing it is better than shipping a step that silently achieves nothing.

In the product

A refusal is a refusal, not a partial install.

denied_keys: 16 refused_step_types: 2 partial_installs: 0 Sample data — illustrative product UI, not a performance claim.

Who it is for

For whoever is accountable for the database

This is the page to read before you let anybody install anything. It is deliberately specific, because we validate packages is not a claim anyone can check.

  • Owners deciding whether installing is safe to delegate
  • Anyone who has to answer where an email claiming to be from them came from
  • Technical readers who want the rule rather than the reassurance

Questions

The things people actually ask.

What is on the deny-list?

Identity fields and workspace-local ids: the owner and creator of anything, the id form of a tag reference, and a reference to a call script that lives in another workspace. The scan is recursive through the whole package, because a denied key nested three levels down is still a denied key.

Why refuse webhook steps instead of checking the address?

Because there is no version of check the address that stays correct. The address is the publisher’s, the payload is your contacts, and any allowlist becomes wrong the moment somebody edits it. Refusing the step type is a rule that cannot rot.

What about the goal types a pack can use?

Only the ones that can be evaluated without an id from somewhere else. A goal that needs a specific form or a specific pipeline stage has no name to fall back on, so it would compile into a branch that can never fire – which is worse than not having the goal at all.

Does this stop packs doing anything useful?

No, and that is the design constraint. The per-recipient email brief a pack designer writes carries no foreign ids and spends nothing on its own, so it lands verbatim. What is stripped is what would have pointed at the wrong workspace, and what is refused is what would have pointed out of yours.

Ask what happens to an owner field on install.

It is the single question that separates a real package boundary from a validation step, and the answer here is that it never arrives.