How Salus works

One request, end to end. Detect inside, tokenise, send tokens, restore inside. The same path for a person, an application or an agent.

The round trip

  1. Your code calls protect. A request containing real values reaches the engine through POST /v1/detect/text (or /v1/detect/file for a document), with a context_id that names the conversation, case or job it belongs to.
  2. Detect. Inside your environment, format rules and language models find the sensitive spans, a normaliser cleans and merges them, and a verifier re-checks every model candidate in its context.
  3. Tokenise. Each value becomes a typed token such as PERSON_kxmrqa. The token is derived from a keyed hash of the value within the context, so the same person gets the same token in every request that shares the context_id. The mapping is written to the vault, a PostgreSQL store in your environment.
  4. Your AI call. Your application sends the tokenised text to its own provider, with its own credentials. Salus does not call the model for you. Only tokens cross the boundary.
  5. Restore. When the answer returns, your code calls POST /v1/recover/text with the same context_id. Tokens in the answer are replaced with the original values, inside your perimeter, and the result is delivered.

A worked example:

input     Müşteri Ayşe Demir (TCKN 12345678901) mart faturasına itiraz ediyor.
protected Müşteri PERSON_kxmrqa (TCKN NATIONAL_ID_wbqtrz) mart faturasına itiraz ediyor.
answer    Sayın PERSON_kxmrqa, mart faturanız incelenmiştir …
restored  Sayın Ayşe Demir, mart faturanız incelenmiştir …

Typed, deterministic tokens

A token carries its class, so the model knows it is looking at a person, an account or a phone number without knowing which one. Within a context the same value always maps to the same token, which is what keeps relationships intact: if a customer appears five times in a thread, the model sees the same token five times and can connect the references.

Across unrelated contexts, and across tenants, the same value gets different tokens. That is a privacy property, not a limitation.

When something fails

Detection is strict by default. If a detector, the verifier or the role layer is down, times out or answers partially, the request fails with 503 and no text is returned. If the text exceeds the model’s sequence limit, the request fails with 422. Nothing is passed through unexamined because a layer was skipped.

Two exceptions are documented honestly. If the vault database is unreachable, detection still succeeds but with ephemeral tokens that cannot be restored, and the response says so in stats.warnings. If the custom-rule database is unreachable, the built-in rules still run. The LiteLLM guardrail path has no such exceptions: it fails closed on any vault or detector problem.

Streaming

Salus endpoints are synchronous: they take complete text and return a complete result. Streaming responses are handled in the LiteLLM guardrail, where the gateway calls Salus incrementally and Salus returns a hold-back count so that a token split across stream chunks is never shown half-restored.

All example values in this documentation are synthetic.