Skip to content

Guarding Pattern: Protect Sensitive Properties

This pattern explains how to use Liteguard when a guard depends on property values that must come from a trusted backend, not from an untrusted client. Typical examples are userId, accountId, plan, entitlement, or any other property a caller could otherwise forge.


When to use this pattern

Use this pattern when:

  • Your rules depend on identity or entitlement data that must be verified server-side.
  • You want to keep matching rules out of the public SDK bundle unless the caller presents a trusted signed context.
  • You want to keep using normal Liteguard rule authoring in the web UI while separating public properties from trusted ones.

If a property is harmless when forged, such as a local UI preference or a non-sensitive experiment bucket, you do not need this pattern.


How it works

Protected properties combine the Liteguard web app, a Protection Key, your backend signer, and the SDK:

  1. The Properties section on the project detail page catalogs SDK property names observed for that project and environment.
  2. You mark a property as Protected in the web UI.
  3. Liteguard removes matching rules for that property from the public bundle served to ordinary SDK clients.
  4. Your backend signs a ProtectedContext payload using a project Protection Key.
  5. The SDK binds that signed payload with bindProtectedContext(...) or withProtectedContext(...).
  6. Liteguard verifies the signature and serves the protected rules only for that signed context.

This lets you keep evaluating guards locally in the SDK while preventing callers from inventing trusted values.


Step 1: Add or confirm the property in the catalog

Open Config, then select your workspace and project. On the project detail page, find the Properties section.

This catalog shows property names Liteguard has already observed from SDK telemetry. It also powers the typeahead suggestions used when you author rules and rate limits.

If the property already appears in the catalog, open it.

If it does not appear yet, you have two options:

  • Exercise the code path once so Liteguard observes it from SDK signals.
  • Click New property to create the property entry before traffic reaches it.

Use the exact same property key in your SDK code and in the Liteguard UI. A rule that references userId matches {"userId": "..."} in code, not {"user_id": "..."}.


Step 2: Mark the property as protected

Open the property detail view and enable Protected property.

When you protect a property, Liteguard warns you which existing rules reference it. After you confirm the change:

  • Matching rules are removed from the public SDK bundle.
  • Public clients can no longer satisfy those rules by passing the property directly.
  • The property remains available for trusted protected-context evaluation.

Unprotecting the property reverses that change and returns matching rules to the public bundle.


Step 3: Author rules as usual

Once a property is protected, you still use it in rules the same way you would use any other property.

For example, you might create a guard rule such as:

  • Property name: plan
  • Operator: in
  • Values: enterprise
  • Result: true

Or a rule such as:

  • Property name: userId
  • Operator: in
  • Values: specific internal user IDs
  • Result: true

The important difference is distribution, not rule authoring. Liteguard will only include those rules in a protected bundle after the SDK binds a valid signed context.


Step 4: Sign a protected context on your backend

Create a backend endpoint or server-side helper that builds a ProtectedContext payload after authenticating the caller and looking up the trusted values.

The payload shape is:

json
{
  "properties": {
    "userId": "user-123",
    "plan": "enterprise"
  },
  "signature": "sha256=...",
  "issuedAt": "2026-01-15T12:00:00Z",
  "expiresAt": "2026-01-15T12:05:00Z"
}

Only properties and signature are required. issuedAt and expiresAt are optional but useful for short-lived assertions.

Store the Protection Key only on trusted backend infrastructure. Never ship it in browser code, mobile bundles, or desktop clients.


Step 5: Bind the protected context in the SDK

After your application receives the signed payload from the backend, bind it to a scope before evaluating guards that depend on protected properties.

Node.js example

ts
import { LiteguardClient } from '@liteguard/liteguard';

const client = new LiteguardClient(process.env.LITEGUARD_TOKEN!);
await client.start();

const publicScope = client.createScope({
  properties: {
    region: req.user.region,
  },
});

const protectedContext = await loadProtectedContextForRequest(req);
const protectedScope = await publicScope.bindProtectedContext(protectedContext);

if (protectedScope.isOpen('billing.enterprise_dashboard')) {
  renderEnterpriseDashboard();
}

Request-scoped Node.js helper

ts
await client.withProtectedContext(protectedContext, async () => {
  if (client.isOpen('billing.enterprise_dashboard')) {
    await renderEnterpriseDashboard();
  }
});

Browser example

ts
import { LiteguardClient } from '@liteguard/liteguard';

const client = new LiteguardClient('pcid-...');
await client.start();

const response = await fetch('/api/liteguard/protected-context', {
  credentials: 'include',
});

const protectedContext = await response.json();
const scope = await client.createScope().bindProtectedContext(protectedContext);

if (scope.isOpen('billing.enterprise_dashboard')) {
  showEnterpriseDashboard();
}

You can continue passing ordinary non-sensitive properties on the same scope with createScope(...) or withProperties(...).


Step 6: Verify the result in the web app

Use two places in Liteguard to confirm the setup:

Properties

On the project detail page, the protected property detail view shows whether the property is protected, which environments it has been observed in, and which rules currently reference it.

Bundle Preview

On the same project detail page, open Bundle Preview.

  • The public preview shows how many protected property names are hidden from public clients.
  • If protected properties are affecting the bundle, the preview lists the omitted property names.
  • The Show protected rules toggle lets operators inspect the protected version of the bundle for debugging. It does not change what ordinary SDK clients receive.

This is the fastest way to confirm that protecting a property actually removed the sensitive rules from the public bundle.


Practical guidance

  • Protect only the properties whose integrity matters. Ordinary properties can stay public and continue working without a backend signer.
  • Keep property names consistent across your code, rules, and signed protected context.
  • Rotate Protection Keys the same way you would rotate any other backend secret.
  • If a protected property goes stale in the catalog, verify that your application is still sending it and that your backend signer is still issuing it.

See also