Skip to content

Content Protection ​

How Sesamy protects premium content on your pages. Capsule encryption is the recommended approach, but several alternatives are available for different use cases.

Capsule uses AES-256-GCM encryption to protect content at rest. The encrypted content is embedded directly in the HTML -- even if someone views the page source, they see only ciphertext. Keys are fetched from the server only after entitlement is verified.

How it works ​

  1. At publish time, your CMS encrypts the premium content and embeds it as a DCA (Digital Content Access) manifest
  2. sesamy-js detects the encrypted content on page load
  3. If the reader is authenticated and entitled, sesamy-js fetches the decryption key and injects the decrypted HTML
  4. If the reader lacks access, Sesamy injects a paywall instead

DCA manifest structure ​

The manifest produced by @sesamy/capsule-server (format 0.10) is a single <script class="dca-manifest"> element. The ciphertext lives inside the manifest JSON -- there is no separate <template> element.

html
<script class="dca-manifest" type="application/json">
  {
    "version": "0.10",
    "resourceJWT": "eyJ...",
    "content": {
      "bodytext": {
        "contentType": "text/html",
        "iv": "base64url-iv...",
        "aad": "scope:premium",
        "ciphertext": "base64url-ciphertext...",
        "wrappedContentKey": [
          { "kid": "260423T10", "iv": "...", "ciphertext": "..." }
        ]
      }
    },
    "issuers": {
      "sesamy": {
        "unlockUrl": "https://api2.sesamy.com/capsule/vendors/YOUR_VENDOR_ID/unlock",
        "keys": [
          {
            "contentName": "bodytext",
            "scope": "premium",
            "contentKey": "...",
            "wrapKeys": [{ "kid": "260423T10", "key": "..." }]
          }
        ]
      }
    }
  }
</script>

sesamy-js also accepts the legacy shape (resource, issuerData) produced by older integrations -- see content/index.ts for the compatibility fallbacks.

The resourceJWT ​

The resourceJWT inside the DCA manifest carries all content metadata: content ID, pass/tier, URL, price, currency, and more. This means you do not need separate <meta> tags for Sesamy content metadata when using Capsule -- the JWT is the single source of truth.

sesamy-js reads the resourceJWT automatically and uses it for entitlement checks, paywall configuration, and analytics. If you need to verify the resourceJWT on your own backend, see Token Validation.

Migrating from meta tags

During the transition, sesamy-js reads metadata from the resourceJWT first and falls back to meta tags when no DCA manifest is present. You can run both systems side by side. See the migration guide for details.

Configuration ​

Enable Capsule in your sesamy-js config:

json
{
  "clientId": "your-vendor-id",
  "vendorId": "your-vendor-id",
  "capsule": { "enabled": true }
}

sesamy-js will automatically:

  • Detect <script class="dca-manifest"> elements on the page
  • Authenticate the user and call the unlock endpoint
  • Decrypt all content items
  • Inject the decrypted HTML into the page

Benefits ​

  • Content is encrypted at rest -- viewing page source reveals nothing
  • Works with static sites and CDNs -- no dynamic rendering required
  • Share links supported via pre-signed tokens
  • Cache-friendly -- encrypted pages can be cached indefinitely
  • Automatic paywall injection when the reader lacks access

Client-side rendering ​

With Capsule, you can cache everything hard because the encrypted HTML is the same for every reader. The decryption and paywall display happen entirely client-side. This is the recommended rendering approach for most publishers.

Locking is handled by Sesamy ​

Because Sesamy controls the unlock decision, you get access to features like paywall strategies (A/B testing different paywalls for different readers) and leaky paywall (granting free access to a set number of articles). These features work transparently with Capsule -- no extra code needed.


Alternative Lock Modes ​

These lock modes use the <sesamy-content-container> web component instead of Capsule. They are still supported and useful for specific scenarios. The component page is the canonical reference for its attributes, events, and lock modes; this section covers how each mode fits a CMS integration.

Proxy Lock Mode ​

Content is stored on the server and fetched only after the user is authenticated and entitled. The page contains only a preview; the full content is never in the HTML source.

html
<sesamy-content-container
  item-src="https://example.com/articles/premium-article"
  publisher-content-id="article-123"
  lock-mode="proxy"
>
  <div slot="preview">
    <p>This is a preview of the premium content...</p>
  </div>
</sesamy-content-container>

Good security, but requires the content to be available via URL for the proxy to fetch. Not suitable for fully static sites.

Encode Lock Mode ​

Content is base64-encoded in the page source and decoded client-side after authentication. Prevents casual copy-paste but is not cryptographically secure -- anyone who reads the page source can decode the content.

html
<sesamy-content-container
  item-src="https://example.com/articles/encoded-article"
  publisher-content-id="article-456"
  lock-mode="encode"
>
  <div slot="preview"><p>Free preview...</p></div>
</sesamy-content-container>

Embed Lock Mode ​

Content is placed in the content slot and only projected into the component's shadow DOM once access is confirmed; when access is denied, the slot element is removed from the DOM. The least secure option -- the content is fully visible in the page source.

html
<sesamy-content-container
  publisher-content-id="article-789"
  lock-mode="embed"
>
  <div slot="preview"><p>Read the first paragraph for free...</p></div>
  <div slot="content"><p>The full premium content...</p></div>
</sesamy-content-container>

Event Lock Mode ​

No content is managed by Sesamy. A sesamyUnlocked event fires on window when the user has access, and you handle the content display yourself. Listen with window.addEventListener -- the event does not propagate through document, so a document.addEventListener handler never runs.

html
<sesamy-content-container
  publisher-content-id="event-101"
  lock-mode="event"
>
  <div slot="preview"><p>Subscribe to unlock this content.</p></div>
</sesamy-content-container>

<script>
  window.addEventListener('sesamyUnlocked', (event) => {
    const { publisherContentId } = event.detail;
    fetchAndRenderContent(publisherContentId);
  });
</script>

Fetch from API ​

For fully custom implementations, skip the web components and use the sesamy-js API directly:

javascript
window.addEventListener('sesamyJsReady', async () => {
  const hasAccess = await window.sesamy.entitlements.hasAccess('premium-article-123');

  if (hasAccess) {
    document.querySelector('.premium-content').style.display = 'block';
  } else {
    document.querySelector('.paywall').style.display = 'block';
  }
});

Comparison ​

StrategySecurityContent in source?CDN-friendlyComplexity
Capsule (DCA)ExcellentEncrypted onlyYesLow (auto-processed)
ProxyGoodNoNo (dynamic fetch)Low
EncodeLowEncoded (reversible)YesLow
EmbedNoneYes (plain text)YesLowest
EventDepends on impl.NoDependsMedium
API fetchDepends on impl.NoDependsHighest

Next Steps ​

Released under the MIT License.