BlkSeal Integration Guide

Overview

BlkSeal integrations usually consist of two parts:

1. Backend Signing

  • Your backend or source system that generates the final content should call the BlkSeal signing API using either the Python SDK or the raw API.
  • Signing should always happen server-side because it requires authenticated OAuth credentials.
  • The generated content and returned signature should then be stored, transmitted, or streamed together to the destination system.

2. Signature Verification

  • The target system that receives the signed content should verify the content against the signature to ensure the content has not been modified.
  • BlkSeal supports both server-to-client and server-to-server verification workflows.

Server-to-Client Verification

  • Your frontend renders the signed content, attaches the returned signature to the message or content element, and uses blkseal-browser.js to automatically verify the content against the BlkSeal public verification endpoint.
  • This is commonly used for AI chat interfaces, streaming assistants, dashboards, notifications, or other browser-rendered content.

Server-to-Server Verification

  • The receiving backend system verifies the signed content directly using the BlkSeal verify API, either through the Python SDK or the raw API.
  • This is commonly used for APIs, webhooks, service integrations, and automated processing systems.

Verification Endpoints

  • BlkSeal provides both public and private verification endpoints.
  • The public verification endpoint does not require authentication, but is rate limited.
  • The private verification endpoint requires OAuth authentication and is governed by your subscription limits.

Back to contents ↑

Quick Start

This section demonstrates the fastest path to generating and verifying your first BlkSeal signature.

Prerequisites

Before continuing, complete the following setup steps:

  1. Create a lyfe.ninja account.
  2. Create an OAuth application.
  3. Create a BlkSeal lease.

Detailed instructions are available in the Account and Developer Setup and Creating a BlkSeal Lease sections.

Install the SDK

pip install git+https://github.com/lyfeninja/lyfeninja_blkseal_python_sdk.git

Configure Credentials

export LYFENINJA_CLIENT_ID="YOUR_CLIENT_ID"
export LYFENINJA_CLIENT_SECRET="YOUR_CLIENT_SECRET"
export LYFENINJA_LEASE_ID="YOUR_LEASE_ID"

Sign Content

import os
from lyfeninja_blkseal import BlkSealClient

client = BlkSealClient(
    client_id=os.getenv("LYFENINJA_CLIENT_ID"),
    client_secret=os.getenv("LYFENINJA_CLIENT_SECRET"),
    default_scope="sign:content verify:content",
)

sign_result = client.sign_text(
    lease_id=os.getenv("LYFENINJA_LEASE_ID"),
    text="Hello World!",
)

signature_b64 = sign_result["signature_b64"]

print(f"request_id: {sign_result['request_id']}")
print(f"signature_prefix: {signature_b64[:32]}...")

Verify Content

verify_result = client.verify_text(
    text="Hello World!",
    signature_b64=signature_b64,
)

print(f"valid: {verify_result['valid']}")

Expected Result

valid: True

Next Steps

Back to contents ↑

Integration Checklist

Use this checklist to confirm your BlkSeal integration is ready for testing or production use.

Account and Access

  • Created a lyfe.ninja account.
  • Enabled developer access.
  • Created an OAuth application.
  • Saved the OAuth client ID and client secret securely.
  • Confirmed the OAuth application has the required scopes, typically sign:content verify:content.

Lease Setup

  • Created a BlkSeal lease.
  • Saved the lease ID for backend signing requests.
  • Confirmed the lease is active and available for signing.

Backend Signing

  • Installed and configured the Python SDK, or prepared raw API calls.
  • Stored OAuth credentials and lease IDs as server-side environment variables.
  • Confirmed signing happens server-side only.
  • Confirmed OAuth client secrets are never exposed to frontend code.
  • Signed only finalized content.
  • Returned or transmitted the generated content and signature together.
  • Sign media bytes instead of URLs whenever practical.

Verification

  • Used the public verification endpoint for browser-based verification.
  • Used the private verification endpoint for authenticated server-side verification.
  • Verified content before displaying, trusting, storing, or processing it in sensitive workflows.
  • Confirmed verification uses the same data_type used during signing.

Browser Verification

  • Included blkseal.css and blkseal-browser.js on pages that display signed content.
  • Configured a BlkSealVerifier instance.
  • Set selector for the outer signed content element.
  • Set contentSelector for the visible content element.
  • Stored the exact signed content in data-blkseal-content whenever possible.
  • Tested selectors using BlkSealDebug.testSelector().
  • Confirmed tamper detection behaves as expected when content changes after verification.
  • Enabled verifyMedia: true when using browser-based media verification.
  • Configure CORS requirements for any external media host when used with browser media verification.

Streaming/SSE Integrations

  • Collected all streamed tokens or chunks into the final assembled text.
  • Signed the final assembled text, not individual tokens.
  • Sent the signed, signature_error, or signature_skipped event before complete.
  • Stored the completed signed content in data-blkseal-content before triggering browser verification.

Files and Large Content

  • Used sign_bytes() or data_type="hash" for files, binary data, and large payloads.
  • Confirmed SHA-256 hashes are generated from the exact bytes being signed or verified.
  • Confirmed the original file contents are not sent to BlkSeal when using hash-based signing.

Operational Readiness

  • Logged signing errors, verification failures, and tamper-detection events where appropriate.
  • Defined what your application should do when verification fails.
  • Tested expired, revoked, missing, and invalid signatures.
  • Confirmed rate limits and subscription limits are acceptable for your expected usage.

Recommendation: Before going live, test one successful signing flow, one successful verification flow, one tampered-content failure, and one signing or verification error path.

Back to contents ↑

Account and Developer Setup

Before integrating BlkSeal, complete the required setup steps in lyfe.ninja.

Step 1: Create an Account

Create a lyfe.ninja account and complete verification steps:
https://lyfe.ninja/accounts/signup/

Step 2: Sign Up as a Developer

Developer access is required to create OAuth applications.

Developer signup:
https://lyfe.ninja/integrations/developer/signup/

Step 3: Create OAuth Application

After developer access is enabled, create an OAuth application: https://lyfe.ninja/integrations/applications/create/

Save the following values:

  • Client ID
  • Client secret
  • Required scopes, typically:
    sign:content verify:content

Applications can be managed here:
https://lyfe.ninja/integrations/applications/
Or by going to your Dashboard and selecting Developer Profile > Manage Applications.

Back to contents ↑

Creating a BlkSeal Lease

What is a Lease?

A lease represents a signing authority within the BlkSeal platform. Every signature generated by BlkSeal is associated with the lease used to create it.

Behind the scenes, leases are linked to a specific BlkBolt encoding model. When content is signed, the lease determines which model is used to generate and verify the signature. This allows signatures to be associated with a particular application, tenant, environment, agent, or other trust boundary.

Leases also provide lifecycle and revocation controls for signing authority without requiring applications to rotate cryptographic key pairs or redistribute public keys.

  • Model Association — Every lease is associated with a specific BlkBolt encoding model used during signing and verification.
  • Soft Revocation — Leases can expire automatically or be revoked manually. Once a lease is no longer active, new signatures cannot be generated using that lease.
  • Lease Rotation — Applications can transition to a new lease while maintaining historical verification records for previously signed content.
  • Verification Metadata — Verification responses include the originating lease identifier, allowing systems to determine which signing authority produced a signature.
  • Multi-Tenant Support — Separate leases can be used for different applications, customers, agents, environments, or workflows.

Revocation Note: Lease expiration and revocation provide a "soft" revocation mechanism by preventing future signatures from being generated with that lease, or preventing existing signatures from validating successfully (although they can still be decoded). Depending on your deployment model, additional controls may also be available to retire or remove the underlying BlkBolt model entirely, providing a stronger form of revocation.

Create a lease for signing content:
https://lyfe.ninja/products/BlkSeal/create_lease/

Save the lease ID. Your backend will pass this lease ID when signing content.

Leases can be managed here:
https://lyfe.ninja/products/BlkSeal/
Or by going to your Dashboard and selecting BlkSeal > Manage Leases.

Back to contents ↑

Raw API Integrations

BlkSeal can be integrated from any language or platform capable of making HTTPS requests.

The Python SDK shown in this guide is a convenience wrapper around the REST API. If you are integrating from another language, you can interact directly with the API endpoints shown below.

Tip: Python developers should generally use the SDK instead of calling the REST API directly. The SDK automatically manages OAuth tokens, retries expired tokens, and provides helper methods for hashing, signing, and verification.

API Reference Documentation

The BlkSeal signature service provides interactive OpenAPI / Swagger documentation for all public endpoints.

API documentation:
https://signatures.lyfe.ninja/docs

The documentation includes request schemas, response schemas, authentication requirements, and an interactive interface for testing API calls directly from your browser.

Tip: If you are integrating BlkSeal from a language that does not yet have an official SDK, the OpenAPI documentation is the best reference for building a custom client.

Step 1: Obtain an OAuth Access Token

Protected endpoints require an OAuth access token obtained using the Client Credentials flow.

curl -X POST https://lyfe.ninja/oauth/token/ \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=sign:content verify:content"

Successful responses return a bearer token similar to:

{
  "access_token": "hwD...",
  "expires_in": 3600,
  "token_type": "Bearer",
  "scope": "sign:content verify:content"
}

Step 2: Sign Content

Use the authenticated signing endpoint to generate a signature for content.

curl -X POST https://signatures.lyfe.ninja/v1/sign \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "lease_id": "YOUR_LEASE_ID",
    "data": "Hello World!",
    "data_type": "string"
  }'

Successful responses return a signature and request metadata.

Step 3: Verify Content (Public Endpoint)

The public verification endpoint does not require authentication and is suitable for browser-based verification and public validation workflows.

curl -X POST https://signatures.lyfe.ninja/v1/verify \
  -H "Content-Type: application/json" \
  -d '{
    "signature_b64": "SIGNATURE_HERE",
    "data": "Hello World!",
    "data_type": "string"
  }'

Step 4: Verify Content (Private Endpoint)

The private verification endpoint requires OAuth authentication and is intended for authenticated server-side integrations.

curl -X POST https://signatures.lyfe.ninja/v1/verify-private \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "signature_b64": "SIGNATURE_HERE",
    "data": "Hello World!",
    "data_type": "string"
  }'

Both verification endpoints return verification results indicating whether the supplied content matches the original signed content along with additional metadata.

Results and metadata may include:

  • request_id — Unique identifier for the signing or verification request. Useful for troubleshooting, auditing, and support inquiries.
  • lease_id — Identifier of the lease used to generate the signature. This can be used to associate signatures with a specific application, tenant, model, or signing authority.
  • create_ts — Timestamp indicating when the signature was created. The value is represented as a Unix timestamp with fractional seconds.
  • encoded_hash — SHA-256 hash of the content provided during signing. This value can be used to independently confirm which content was processed without exposing the original content.
  • valid — Verification result indicating whether the supplied content matches the original signed content. A value of true indicates successful verification, while false indicates the content, signature, or metadata does not match.

Supported Data Types

BlkSeal supports signing both raw content and pre-computed hashes.

The appropriate option depends on your use case.

String Data

Use data_type="string" when signing text directly. This is the most common option for AI-generated content, chat messages, notifications, emails, API responses, and other text-based content.

{
  "data": "Hello World!",
  "data_type": "string"
}

Hash Data

Use data_type="hash" when signing a SHA-256 hash instead of the original content. This is useful for large files, binary content, images, PDFs, documents, or situations where transmitting the original content to BlkSeal is undesirable.

{
  "data": "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e",
  "data_type": "hash"
}

Recommendation: Use string for most AI, messaging, and web application integrations. Use hash when signing files, binary data, or large payloads.

Important Notes

  • Verification must use the same data_type that was used during signing.
  • Hash values should be SHA-256 encoded as lowercase hexadecimal strings.
  • Any modification to the original content or hash value will cause verification to fail.

Back to contents ↑

Installing and configuring the Python SDK

The BlkSeal Python SDK provides a simple interface for signing and verifying content using your OAuth credentials and lease.

Step 1: Install the SDK

Install the latest version directly from GitHub:

pip install git+https://github.com/lyfeninja/lyfeninja_blkseal_python_sdk.git

Alternatively, you can clone the repository directly into your project. This is often useful when reviewing examples or making local modifications.

git clone https://github.com/lyfeninja/lyfeninja_blkseal_python_sdk.git

PyPI support is planned for a future release.

Step 2: Configure Credentials

The SDK uses OAuth credentials created during the developer setup process. We recommend storing these values as environment variables.

LYFENINJA_CLIENT_ID=your_client_id
LYFENINJA_CLIENT_SECRET=your_client_secret
LYFENINJA_LEASE_ID=your_lease_id

Step 3: Create a BlkSeal Client

Create a client instance that can be reused throughout your application.

import os
import lyfeninja_blkseal

client = lyfeninja_blkseal.BlkSealClient(
     client_id=os.getenv("LYFENINJA_CLIENT_ID"),
     client_secret=os.getenv("LYFENINJA_CLIENT_SECRET"),
     default_scope="sign:content verify:content",
)

Recommended: Reusable Client Pattern

For web applications and APIs, it is recommended to create the SDK client once and reuse it across requests.

import os
from functools import lru_cache
from lyfeninja_blkseal import BlkSealClient

@lru_cache(maxsize=1)
def get_blkseal_client():
     client_id = os.getenv("LYFENINJA_CLIENT_ID")
     client_secret = os.getenv("LYFENINJA_CLIENT_SECRET")

     if not client_id or not client_secret:
          return None

     return BlkSealClient(
          client_id=client_id,
          client_secret=client_secret,
          default_scope="sign:content verify:content",
)

client = get_blkseal_client()

Using lru_cache(maxsize=1) creates the SDK client lazily and reuses it across requests, reducing initialization overhead and keeping your integration code simple.

Tip: Create the client once during application startup or cache it using a pattern similar to the example above. Avoid creating a new SDK client for every request.

The SDK automatically manages OAuth token retrieval and refresh as needed.

You can test retrieving a token with the following.

token = client.get_token()
print("Token retrieved successfully")
print(f"token_prefix: {token[:8]}...")

Step 4: Test your configuration

Test signing text with the following.

lease_id = os.getenv("LYFENINJA_LEASE_ID")
content = 'Hello World!'

sign_result = client.sign_text(
     lease_id=lease_id,
     text=content,
)

print(f"request_id: {sign_result['request_id']}")
print(f"signature_prefix: {sign_result['signature_b64'][0:4]}")

Test verifying text with the following.

# verify with public endpoint
verify_result = client.verify_text(
     text=content,
     signature_b64=sign_result['signature_b64'],
)

print(verify_result)

# verify with private endpoint (requires authentication)
private_verify_result = client.verify_text(
     text=content,
     signature_b64=sign_result['signature_b64'],
     private=True,
)

print(private_verify_result)

Signing and Verifying Media by URL

The SDK includes convenience helpers that can download a public resource, compute its SHA-256 hash, and sign or verify the resulting hash.

This is useful for quickly signing images, PDFs, videos, documents, or other publicly accessible files.

media_url = "https://example.com/image.png"

sign_result = client.sign_url(
    lease_id=os.getenv("LYFENINJA_LEASE_ID"),
    url=media_url,
)

verify_result = client.verify_url(
    url=media_url,
    signature_b64=sign_result["signature_b64"],
)

print(verify_result["valid"])

Recommendation: The URL helpers are convenient for testing and simple integrations, but production systems should generally sign the original bytes used to create the media rather than signing a URL. This avoids dependencies on external hosting infrastructure and guarantees that the exact content being served was signed.

Signing and Verifying Media Bytes

For images, PDFs, documents, audio, video, and other binary content, the recommended workflow is to sign the file bytes directly.

with open("image.png", "rb") as f:
    image_bytes = f.read()

sign_result = client.sign_bytes(
    lease_id=os.getenv("LYFENINJA_LEASE_ID"),
    data=image_bytes,
)

verify_result = client.verify_bytes(
    data=image_bytes,
    signature_b64=sign_result["signature_b64"],
)

print(verify_result["valid"])

Back to contents ↑

Frontend Browser Verification Helpers

This section applies to browser-based integrations using blkseal.css and blkseal-browser.js.

The BlkSeal browser helpers provide automatic signature verification, verification badges, tamper detection, manual re-verification, and frontend content extraction tools.

Include the Browser Libraries

Include blkseal.css and blkseal-browser.js on any page that displays signed content.

<link rel="stylesheet" href="https://lyfeninja-static.storage.googleapis.com/blkseal/v1/blkseal.css">
<script src="https://lyfeninja-static.storage.googleapis.com/blkseal/v1/blkseal-browser.js"></script>

Recommended Message HTML

Each signed message should have an outer container and a child element containing the visible content.

<div class="ai-message">
  <div class="message-content">Signed content appears here</div>
</div>

Browser Media Verification

BlkSeal can also verify images, PDFs, videos, audio files, iframes, embeds, links, and other media directly in the browser.

When enabled, the browser downloads the media, computes a SHA-256 hash locally, and verifies that hash against the supplied BlkSeal signature.

Supported Media Elements

  • <img>
  • <video>
  • <audio>
  • <iframe>
  • <embed>
  • <object>
  • <a>

Required Media Attributes

The browser verifier expects signed media elements to contain a BlkSeal media signature.

<img
  src="/images/logo.png"
  data-blkseal-media-signature="BASE64_SIGNATURE"
/>

CORS Requirements

The browser verifier downloads media directly in order to compute a SHA-256 hash. The media host must therefore allow cross-origin requests from the application performing verification.

If media verification consistently fails while the file loads correctly in the browser, verify that your hosting platform includes appropriate CORS headers.

Common hosting providers requiring CORS configuration include:

  • Google Cloud Storage
  • Amazon S3
  • Cloudflare R2
  • Azure Blob Storage
  • Custom CDN deployments

The browser verifier fetches media using standard CORS requests and cannot hash resources that are blocked by the browser's same-origin policy.

Initialize the Verifier

Create a BlkSealVerifier instance after the browser library has loaded.

const blksealVerifier = new BlkSealVerifier({
  selector: ".ai-message",
  contentSelector: ".message-content",
  verifyUrl: "https://signatures.lyfe.ninja/v1/verify",
  verifyMedia: true,
  mediaSignatureAttribute: "data-blkseal-media-signature",
  mediaStatusAttribute: "data-blkseal-media-status",
  debug: false
});

Recommended Pattern: Store the Exact Signed Content

Whenever possible, store the exact signed content in a dedicated attribute using data-blkseal-content.

<div
  class="ai-message"
  data-signature="..."
  data-blkseal-content="Exact signed content"
>
  <div class="message-content">Exact signed content</div>
</div>

This approach is recommended because it:

  • Avoids ambiguity caused by HTML formatting or rendering.
  • Makes the original signed payload explicit.
  • Improves reliability when working with Markdown or rich content.
  • Enables visible-content tamper detection.

Verifier Configuration Options

The BlkSealVerifier constructor accepts a configuration object that controls how content is located, extracted, verified, and monitored for changes.

Option Required Description
selector Yes CSS selector used to identify the outer container for signed content. Verification badges and status information are attached to these elements.
contentSelector Usually CSS selector used to locate the element containing the visible content. If omitted, the entire message element is used.
explicitContentAttribute Recommended Attribute containing the exact content that was signed. Using this option helps prevent verification failures caused by HTML rendering, formatting changes, or browser modifications.
contentMode No Controls how visible content is extracted. Supported values are textContent, innerText, and paragraphs.
paragraphSelector No CSS selector used to locate paragraphs when contentMode: "paragraphs" is enabled.
paragraphJoiner No String inserted between extracted paragraphs when rebuilding content for verification.
hideOnFailure No When enabled, hides content if verification fails or tampering is detected.
showBadges No Controls whether verification status badges are displayed. When set to false, verification still occurs in the background and hideOnFailure continues to work, but users cannot manually trigger reverification through badge clicks.
allowBadgeReverify No Allows users to click verification badges to manually trigger a new verification request.
watchContentChanges No Monitors the DOM for content changes after verification and automatically marks modified content as stale or tampered.
verifyMedia No Enables automatic browser verification of signed media such as images, videos, audio files, PDFs, embedded documents, and links.
mediaSignatureAttribute No Attribute containing the BlkSeal signature for media elements. Defaults to data-blkseal-media-signature.
mediaStatusAttribute No Attribute used to communicate media signing state and verification status. Defaults to data-blkseal-media-status.
mediaSelector No Custom selector used to identify signed media elements. If omitted, BlkSeal automatically scans supported media types including images, videos, audio, iframes, embeds, objects, and links.
debug No Enables detailed console logging to assist with troubleshooting and integration testing.

Recommended Configuration: For most integrations, set selector, contentSelector, and explicitContentAttribute. These options provide the most reliable browser verification experience.

Media Verification Note: When verifyMedia is enabled, the browser fetches the media file and computes a SHA-256 hash locally before verification. If the media file is hosted on a different domain, bucket, CDN, or static hosting service, that host must allow cross-origin requests from the page performing verification. A media file may still display normally even when browser-based verification fails because the verification request is subject to CORS rules.

Selector-Based Extraction

If the rendered DOM is considered the source of truth, the verifier can extract content directly from the page using CSS selectors.

const blksealVerifier = new BlkSealVerifier({
  selector: ".ai-message",
  contentSelector: ".message-content",
  contentMode: "textContent"
});

Paragraph-Based Extraction

Paragraph extraction is useful when content is rendered from Markdown or other rich text systems that generate multiple paragraph elements.

<div class="markdown-content">
  <p>First paragraph</p>
  <p>Second paragraph</p>
</div>

Configure the verifier as follows:

const blksealVerifier = new BlkSealVerifier({
  selector: ".message-with-markdown",
  contentSelector: ".markdown-content",
  contentMode: "paragraphs",
  paragraphSelector: "p",
  paragraphJoiner: "\\n\\n"
});

The extracted content would be:

First paragraph

Second paragraph

Testing Selectors

The browser library includes a debugging helper named BlkSealDebug.testSelector(). Run it from your browser's developer console to confirm your selectors are extracting the expected content.

BlkSealDebug.testSelector({
  selector: ".flex-row:has(.markdown-content)",
  contentSelector: ".markdown-content",
  contentMode: "paragraphs",
  paragraphSelector: "p",
  paragraphJoiner: "\\n\\n"
});

The debugging helper logs:

  • Number of matching elements.
  • The selected content element.
  • The extracted text.
  • The extraction mode being used.

Tip: If browser verification is failing unexpectedly, use BlkSealDebug.testSelector() and compare the extracted content against the original signed content. Most browser-side verification issues are caused by content extraction mismatches rather than invalid signatures.

Back to contents ↑

Flow Diagrams

Server to Server Flow Diagram

BlkSeal Server to Server Diagram

Server to Browser Flow Diagram

BlkSeal Server to Browser Diagram

Back to contents ↑

Non-Streaming Integrations

Use this pattern when your application generates the complete response before delivering it to the client or another system.

Common examples include AI chat responses, API responses, notifications, reports, webhooks, and document generation workflows.

Architecture

Application
  ↓
Generate Content
  ↓
Sign Content
  ↓
Content + Signature
  ↓
Destination System
  ↓
Verify Content
  ↓
Trusted / Untrusted

Integration Flow

  1. Generate the final content.
  2. Sign the content using BlkSeal.
  3. Deliver the content and signature together.
  4. Verify the content before displaying or processing it.
  5. Surface the verification result to the user or downstream system.

Server-to-Browser Integration for Text

This pattern is commonly used when your backend generates content and your frontend displays it to a user.

Backend Example (Python)

import os

def generate_response(request):
     final_text = generate_content_somehow()

     blkseal_client = get_blkseal_client()

     sign_response = blkseal_client.sign_text(
          lease_id=os.getenv("LYFENINJA_LEASE_ID"),
          text=final_text,
     )

     # Return or transmit both values together
     # Most integrations will serialize this structure as JSON.
     response_payload = {
            "content": final_text,
          "signature": sign_response,
     }

     return response_payload

Expected Response Shape

{
  "content": "Final generated content",
  "signature": {
    "request_id": "...",
    "signature_b64": "..."
  }
}

Frontend Example

Tip: Using the browser libraries is the easiest way to add visual verification indicators and tamper detection to your application. The libraries automatically verify content against the BlkSeal public verification endpoint and display verification results directly to users.

Note: This browser example assumes blkseal.css, blkseal-browser.js, and a BlkSealVerifier instance have already been configured. See Frontend Browser Verification Helpers.

const blksealVerifier = new BlkSealVerifier({
  selector: ".ai-message",
  contentSelector: ".message-content",
  explicitContentAttribute: "data-blkseal-content",
  verifyUrl: "https://signatures.lyfe.ninja/v1/verify",
  hideOnFailure: false,
  allowBadgeReverify: true,
  watchContentChanges: true,
  debug: false
});

async function renderSignedContent() {
  const response = await fetch("/generate");
  const data = await response.json();

  const messageEl = document.querySelector(".ai-message");
  const contentEl = messageEl.querySelector(".message-content");

  contentEl.textContent = data.content;

  // Store the exact signed content.
  messageEl.setAttribute("data-blkseal-content", data.content);

  blksealVerifier.handleSignedEvent(messageEl, {
    signature: data.signature
  });
}

Important: Always store or verify the exact content that was signed. Verification may fail if the content is changed after signing, including whitespace changes, truncation, text formatting, or other transformations that alter the underlying text.

Server-to-Browser Integration for Media

Media such as images, PDFs, videos, audio files, and other binary content can be signed and verified using the same workflow as text content.

In this pattern, the backend signs the original media bytes and delivers both the media URL and signature to the browser. The browser then downloads the media, computes a SHA-256 hash locally, and verifies the media against the supplied BlkSeal signature.

Recommendation: Sign the original file bytes whenever possible using sign_bytes(). While the SDK also provides sign_url() and verify_url() helpers that fetch a URL and hash its bytes, signing the original bytes directly is preferred when your application already has access to the file. This ensures the exact content being distributed is what was originally signed.

Backend Example (Python)

with open("logo.png", "rb") as f:
    image_bytes = f.read()

sign_result = blkseal_client.sign_bytes(
    lease_id=os.getenv("LYFENINJA_LEASE_ID"),
    data=image_bytes,
)

response_payload = {
    "image_url": uploaded_image_url,
    "image_signature": sign_result["signature_b64"]
}

Frontend Example

Note: Media verification requires verifyMedia: true in your BlkSealVerifier configuration. See Frontend Browser Verification Helpers for complete setup instructions.

<img
  src=""
  data-blkseal-media-signature=""
/>

Verification Flow

  1. The browser discovers a supported media element containing a BlkSeal media signature.
  2. The media is downloaded directly from its source URL.
  3. A SHA-256 hash is computed locally in the browser.
  4. The hash is verified with the signature against the BlkSeal public verification endpoint.
  5. A verification badge is displayed to the user.

CORS Requirement: Because media verification fetches the media file and computes a SHA-256 hash locally in the browser, media hosted on a different domain, bucket, CDN, or static hosting service must allow cross-origin requests from the page performing verification. A media file may still display normally even when browser-based verification fails because the verification request is subject to CORS rules. See Frontend Browser Verification Helpers for additional details.

Server-to-Server Integration

Server-to-server integrations are commonly used for APIs, webhooks, agent-to-agent communication, workflow automation, and internal microservices.

In this model, the receiving system verifies the content before processing it.

Sending Service

payload = {
    "message": generated_content,
}

sign_result = blkseal_client.sign_text(
     lease_id=os.getenv("LYFENINJA_LEASE_ID"),
     text=payload["message"],
)

payload["signature"] = sign_result

send_to_partner_system(payload)

Payload Example

{
  "message": "Hello from Service A",
  "signature": {
    "request_id": "...",
    "signature_b64": "..."
  }
}

Receiving Service

verify_result = blkseal_client.verify_text(
     text=payload["message"],
     signature_b64=payload["signature"]["signature_b64"],
     private=True,
)

if not verify_result["valid"]:
     raise Exception("Content verification failed")

process_message(payload["message"])

Recommended Failure Handling

If verification fails, the receiving system should treat the content as untrusted.

  • Reject the request
  • Log the verification failure
  • Alert operators or administrators
  • Mark the content as untrusted
  • Request retransmission from the source system

The appropriate response depends on the sensitivity of the workflow and the level of trust required by your application.

Back to contents ↑

Streaming/SSE Integrations

Use this pattern when your application streams generated content token-by-token or chunk-by-chunk before the final response is complete.

This pattern is commonly used for AI chat interfaces, assistants, live generation workflows, and other applications where users see content appear progressively.

Recommended Event Order for Text Streams

For text-only streaming integrations, the recommended event order is:

start
token
token
token
signed OR signature_error OR signature_skipped
complete

Important: Sign the final assembled content, then send the signed event before sending complete. This allows the browser to verify the final message as soon as the stream finishes.

Backend SSE Helper

Server-Sent Events require each event to include an event name and JSON payload.

import json

def sse_event(event: str, payload: dict) -> str:
    return f"event: {event}\ndata: {json.dumps(payload)}\n\n"

Backend Streaming Example

The backend should collect streamed chunks, assemble the final text, sign the final text, and then emit a signing result event before closing the stream.

This example uses the BlkSeal Python SDK within a Django application and Server-Sent Events (SSE). The same signing pattern can be adapted to other frameworks such as FastAPI, Flask, Express.js, .NET, Go, or any platform capable of streaming content.

import os
from typing import Iterator
from django.http import StreamingHttpResponse
from lyfeninja_blkseal import (
    BlkSealError,
    BlkSealAuthError,
    BlkSealAPIError,
    BlkSealInputError,
)

def stream_response(request):
    def event_stream() -> Iterator[str]:
        final_parts = []

        yield sse_event("start", {"ok": True})

        for token in stream_model_response():
            final_parts.append(token)
            yield sse_event("token", {"text": token})

        final_text = "".join(final_parts).strip()

        if not final_text:
            yield sse_event("error", {"error": "Empty response from model"})
            return

        message_id = save_message_somehow(final_text)
        sign_response = None
        blkseal_client = get_blkseal_client()

        if blkseal_client is None:
            yield sse_event("signature_skipped", {
                "message_id": message_id,
                "reason": "not_configured",
            })
        else:
            try:
                sign_response = blkseal_client.sign_text(
                    lease_id=os.getenv("LYFENINJA_LEASE_ID"),
                    text=final_text,
                )

                yield sse_event("signed", {
                    "message_id": message_id,
                    "signature": sign_response,
                })

            except BlkSealAuthError:
                yield sse_event("signature_error", {"message_id": message_id, "reason": "auth_failed"})

            except BlkSealInputError:
                yield sse_event("signature_error", {"message_id": message_id, "reason": "invalid_input"})

            except BlkSealAPIError as e:
                yield sse_event("signature_error", {
                    "message_id": message_id,
                    "reason": "api_failed",
                    "detail": str(e),
                })

            except BlkSealError:
                yield sse_event("signature_error", {"message_id": message_id, "reason": "signing_failed"})

            except Exception:
                yield sse_event("signature_error", {"message_id": message_id, "reason": "unexpected_error"})

        yield sse_event("complete", {
            "message_id": message_id,
            "content": final_text,
            "signed": sign_response is not None,
        })

    response = StreamingHttpResponse(event_stream(), content_type="text/event-stream")
    response["Cache-Control"] = "no-cache"
    response["X-Accel-Buffering"] = "no"
    return response

Frontend Streaming Example

The frontend should render tokens as they arrive, then attach the final signature to the completed message when the signed event is received.

Tip: Using the browser libraries is the easiest way to add visual verification indicators and tamper detection to your application. The libraries automatically verify content against the BlkSeal public verification endpoint and display verification results directly to users.

Note: This browser example assumes blkseal.css, blkseal-browser.js, and a BlkSealVerifier instance have already been configured. See Frontend Browser Verification Helpers.

Recommended Message HTML

<div class="chat-bubble chat-bubble-assistant">
  <div class="chat-message-content">
    Assistant response appears here.
  </div>
</div>

Initialize Verifier

const blksealVerifier = new BlkSealVerifier({
  selector: ".chat-bubble-assistant",
  contentSelector: ".chat-message-content",
  explicitContentAttribute: "data-blkseal-content",
  verifyUrl: "https://signatures.lyfe.ninja/v1/verify",
  hideOnFailure: true,
  allowBadgeReverify: true,
  watchContentChanges: true,
  verifyMedia: true,
  debug: false
});

Media Verification: If your stream can return signed media, enable verifyMedia: true. See Frontend Browser Verification Helpers for the full browser helper setup and CORS requirements.

SSE Event Listeners

const eventSource = new EventSource("/stream?message=hello");

const assistantBubble = document.querySelector(".chat-bubble-assistant");
const assistantContent = assistantBubble.querySelector(".chat-message-content");

eventSource.addEventListener("token", (e) => {
  const data = JSON.parse(e.data);
  assistantContent.textContent += data.text;
});

eventSource.addEventListener("signed", (e) => {
  const data = JSON.parse(e.data);

  // Store the exact signed content on the message element.
  assistantBubble.setAttribute(
    "data-blkseal-content",
    assistantContent.textContent
  );

  blksealVerifier.handleSignedEvent(assistantBubble, data);
});

eventSource.addEventListener("signature_error", (e) => {
  let data = {};

  try {
    data = JSON.parse(e.data);
  } catch (_) {}

  blksealVerifier.handleSignatureErrorEvent(assistantBubble, data);
});

eventSource.addEventListener("signature_skipped", (e) => {
  let data = {};

  try {
    data = JSON.parse(e.data);
  } catch (_) {}

  blksealVerifier.handleSignatureSkippedEvent(assistantBubble, data);
});

eventSource.addEventListener("complete", () => {
  eventSource.close();
});

Best Practice: The browser should verify the exact final text that was signed by the backend. For streaming integrations, this usually means waiting until the signed event, storing the completed message in data-blkseal-content, and then triggering verification.

Streaming Signed Media

Streaming applications can also deliver signed media alongside signed text responses. This is useful for AI assistants, generated reports, image generation workflows, document previews, audio/video responses, and other applications where the final response may include both text and media.

The recommended pattern is to stream the visible text normally, then stream media metadata and media signatures as separate SSE events.

Recommended Event Order with Media

When a stream includes both signed text and signed media, a typical event order is:

start
token
token
media
media_signed OR media_signature_error OR media_signature_skipped
signed OR signature_error OR signature_skipped
complete

Recommendation: Sign the final assembled text for message verification, and sign the original media bytes for media verification. Avoid signing the media URL itself unless you specifically want to verify the URL string rather than the file content.

Backend Media Signing Example

In this example, the backend reads the generated media bytes, signs the bytes, then streams both the media URL and the media signature to the browser.

def stream_media_response():
    message_id = save_message_somehow("Here is a signed image.")
    image_url = upload_or_get_media_url("generated_image.png")

    yield sse_event("media", {
        "message_id": message_id,
        "media": {
            "kind": "image",
            "url": image_url,
            "mime_type": "image/png",
            "alt": "Generated signed image",
            "caption": "Signed image verified by BlkSeal."
        }
    })

    try:
        with open("generated_image.png", "rb") as f:
            image_bytes = f.read()

        sign_result = blkseal_client.sign_bytes(
            lease_id=os.getenv("LYFENINJA_LEASE_ID"),
            data=image_bytes,
        )

        yield sse_event("media_signed", {
            "message_id": message_id,
            "signature": sign_result,
        })

    except BlkSealError:
        yield sse_event("media_signature_error", {
            "message_id": message_id,
            "reason": "signing_failed",
        })

Media Rendering Helper

function renderMedia(container, media) {
  const wrapper = document.createElement("div");
  wrapper.className = "blkseal-stream-media";

  let mediaEl;

  if (media.kind === "image") {
    mediaEl = document.createElement("img");
    mediaEl.src = media.url;
    mediaEl.alt = media.alt || "";
  } else if (media.kind === "video") {
    mediaEl = document.createElement("video");
    mediaEl.src = media.url;
    mediaEl.controls = true;
  } else if (media.kind === "audio") {
    mediaEl = document.createElement("audio");
    mediaEl.src = media.url;
    mediaEl.controls = true;
  } else if (media.kind === "pdf") {
    mediaEl = document.createElement("a");
    mediaEl.href = media.url;
    mediaEl.target = "_blank";
    mediaEl.rel = "noopener noreferrer";
    mediaEl.textContent = media.caption || "Open signed PDF";
  } else {
    mediaEl = document.createElement("a");
    mediaEl.href = media.url;
    mediaEl.target = "_blank";
    mediaEl.rel = "noopener noreferrer";
    mediaEl.textContent = media.caption || "Open signed media";
  }

  if (media.mime_type) {
    mediaEl.dataset.mimeType = media.mime_type;
  }

  wrapper.appendChild(mediaEl);

  if (media.caption && media.kind !== "pdf") {
    const caption = document.createElement("div");
    caption.className = "blkseal-stream-media-caption";
    caption.textContent = media.caption;
    wrapper.appendChild(caption);
  }

  container.appendChild(wrapper);
  return mediaEl;
}

SSE Event Listeners

const eventSource = new EventSource("/stream?message=hello");

const assistantBubble = document.querySelector(".chat-bubble-assistant");
const assistantContent = assistantBubble.querySelector(".chat-message-content");

const mediaByMessageId = new Map();

eventSource.addEventListener("token", (e) => {
  const data = JSON.parse(e.data);
  assistantContent.textContent += data.text;
});

eventSource.addEventListener("media", (e) => {
  const data = JSON.parse(e.data);
  const media = data.media;

  const mediaEl = renderMedia(assistantContent, media);

  if (data.message_id) {
    mediaByMessageId.set(data.message_id, mediaEl);
  }
});

eventSource.addEventListener("media_signed", (e) => {
  const data = JSON.parse(e.data);
  const mediaEl = mediaByMessageId.get(data.message_id);

  if (!mediaEl || !data.signature) return;

  mediaEl.setAttribute(
    "data-blkseal-media-signature",
    data.signature.signature_b64
  );

  if (blksealVerifier.mediaVerifier) {
    blksealVerifier.mediaVerifier.reverifyMediaElement(mediaEl);
  }
});

eventSource.addEventListener("media_signature_error", (e) => {
  let data = {};

  try {
    data = JSON.parse(e.data);
  } catch (_) {}

  const mediaEl = mediaByMessageId.get(data.message_id);

  if (mediaEl) {
    mediaEl.setAttribute("data-blkseal-media-status", "signature_error");
    if (blksealVerifier.mediaVerifier) {
      blksealVerifier.mediaVerifier.scan(document);
    }
  }
});

eventSource.addEventListener("media_signature_skipped", (e) => {
  let data = {};

  try {
    data = JSON.parse(e.data);
  } catch (_) {}

  const mediaEl = mediaByMessageId.get(data.message_id);

  if (mediaEl) {
    mediaEl.setAttribute("data-blkseal-media-status", "signature_skipped");
    if (blksealVerifier.mediaVerifier) {
      blksealVerifier.mediaVerifier.scan(document);
    }
  }
});

eventSource.addEventListener("signed", (e) => {
  const data = JSON.parse(e.data);

  // Store the exact final text that was signed by the backend.
  assistantBubble.setAttribute(
    "data-blkseal-content",
    assistantContent.textContent
  );

  blksealVerifier.handleSignedEvent(assistantBubble, data);
});

eventSource.addEventListener("signature_error", (e) => {
  let data = {};

  try {
    data = JSON.parse(e.data);
  } catch (_) {}

  blksealVerifier.handleSignatureErrorEvent(assistantBubble, data);
});

eventSource.addEventListener("signature_skipped", (e) => {
  let data = {};

  try {
    data = JSON.parse(e.data);
  } catch (_) {}

  blksealVerifier.handleSignatureSkippedEvent(assistantBubble, data);
});

eventSource.addEventListener("complete", () => {
  eventSource.close();
});

CORS Requirement: Browser media verification requires the browser to fetch the media file and compute a SHA-256 hash locally. If the media file is hosted on a different domain, bucket, CDN, or static hosting service, that host must allow cross-origin requests from the page performing verification. A media file may still display normally even when browser-based verification fails because the verification request is subject to CORS rules. See Frontend Browser Verification Helpers.

Back to contents ↑

Tamper Detection and Badge States

This section applies to browser-based integrations using blkseal-browser.js.

When watchContentChanges: true is enabled, the browser verifier watches signed content for DOM or text changes after verification.

How Tamper Detection Works

The most reliable tamper detection pattern is to store the exact signed content in data-blkseal-content.

data-blkseal-content="original signed content"

If the visible message content changes and no longer matches the explicit signed content, the verifier marks the message as tampered.

Content Tampered

If the content changes but no explicit signed content is available, the verifier cannot determine whether the change is malicious or expected. In that case, the badge changes to:

Content Changed - Reverify

If allowBadgeReverify: true is enabled, users can click the badge to run verification again.

Recommended Configuration

const blksealVerifier = new BlkSealVerifier({
  selector: ".ai-message",
  contentSelector: ".message-content",
  explicitContentAttribute: "data-blkseal-content",
  watchContentChanges: true,
  allowBadgeReverify: true,
  hideOnFailure: false
});

Best Practice: Use data-blkseal-content whenever possible. This allows the browser verifier to compare the visible rendered content against the exact content that was signed.

Badge States

The default BlkSeal CSS includes badge styles for common verification, signing, and tamper-detection states.

Badge Meaning
Verifying... Verification request is currently in progress.
Verified The content and signature match.
Verification Failed The verification API responded successfully, but the content did not match the signature.
Verification Error The verification request could not be completed, usually because of a network, endpoint, or response error.
Signature Error Signing failed server-side, so no usable signature was returned.
Unsigned Signing was skipped, disabled, or not configured for this response.
Content Changed - Reverify The DOM changed after verification. The user can re-run verification if badge re-verification is enabled.
Content Tampered The visible content no longer matches the explicit signed content stored in data-blkseal-content.

Failure Handling

By default, failed or tampered content remains visible with a warning badge. For stricter applications, enable hideOnFailure to hide content when verification fails.

const blksealVerifier = new BlkSealVerifier({
  selector: ".ai-message",
  contentSelector: ".message-content",
  hideOnFailure: true
});

The right failure behavior depends on your use case. Low-risk applications may only need to show a warning badge, while high-trust workflows may choose to hide or block unverified content.

Back to contents ↑

Security Notes

BlkSeal helps verify that signed content still matches the content originally signed by the source system.

Browser verification is a useful trust signal, but it is not a tamper-proof execution environment.

What Browser Verification Can Help Detect

  • Browser extension manipulation
  • DOM tampering
  • Injected scripts
  • Accidental frontend changes
  • Content altered after signing
  • Content rendered differently than the signed payload

Important Limitations

A user with full control of their browser, developer tools, local JavaScript runtime, or device can modify the page, disable JavaScript, block network calls, or alter local state.

For this reason, browser-side verification should not be treated as the only control for high-assurance workflows.

Recommended Practices

  • Keep OAuth client secrets server-side only.
  • Sign finalized content before delivering it to the client or receiving system.
  • Use data-blkseal-content to preserve the exact signed payload for browser verification.
  • Use server-side verification for sensitive actions, transactions, approvals, or audit workflows.
  • Log verification failures, signature errors, and tamper-detection events when appropriate.
  • Use the public verification endpoint for browser validation and private verification for authenticated backend workflows.

High-Assurance Workflows: For sensitive use cases, provide server-side verification, an external verification page, or both. Browser badges are helpful for user-facing transparency, but backend verification should be used when verification results affect access, payments, approvals, compliance records, or other important decisions.

Back to contents ↑

Signing Files, Binary Data, and Large Content

Use this pattern when signing files, binary data, large payloads, images, PDFs, documents, archives, model artifacts, or other content that should not be sent directly to the signing service.

Instead of sending the original bytes, compute a SHA-256 hash of the content and sign the hash using data_type="hash".

Important: The BlkSeal Python SDK automatically hashes bytes locally when using sign_bytes() or verify_bytes(). The original file contents are not sent to the BlkSeal signing or verification service.

Python SDK Example

Sign a File

# Open file as bytes
with open("document.pdf", "rb") as f:
    file_bytes = f.read()

# The SDK hashes the bytes locally and signs the hash.
response = client.sign_bytes(
    lease_id=lyfeninja_lease_id,
    data=file_bytes,
)

signature_b64 = response["signature_b64"]

Verify a File

# Open file as bytes
with open("document.pdf", "rb") as f:
    file_bytes = f.read()

# The SDK hashes the bytes locally and verifies the hash.
result = client.verify_bytes(
    data=file_bytes,
    signature_b64=signature_b64,
)

print(result)

Raw API Example

Without the SDK, compute the SHA-256 hash of the file yourself, then call the signing or verification endpoint with data_type="hash".

Compute a SHA-256 Hash

import hashlib

with open("document.pdf", "rb") as f:
    file_bytes = f.read()

file_hash = hashlib.sha256(file_bytes).hexdigest()

print(file_hash)

Sign the Hash

Send the hash value to the signing endpoint with data_type set to hash.

curl -X POST https://signatures.lyfe.ninja/v1/sign \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "lease_id": "YOUR_LEASE_ID",
    "data": "SHA256_HASH_HEX_VALUE",
    "data_type": "hash"
  }'

Verify the Hash

To verify the file later, compute the SHA-256 hash of the current file bytes and submit that hash with the original signature.

curl -X POST https://signatures.lyfe.ninja/v1/verify \
  -H "Content-Type: application/json" \
  -d '{
    "signature_b64": "SIGNATURE_HERE",
    "data": "SHA256_HASH_HEX_VALUE",
    "data_type": "hash"
  }'

What Gets Sent to BlkSeal Signature Service?

Operation What is sent to BlkSeal
sign_text() Original text content
verify_text() Original text content
sign_bytes() SHA-256 hash of the bytes
verify_bytes() SHA-256 hash of the bytes
Raw API with data_type="hash" SHA-256 hash value

Recommendation: Use sign_text() for ordinary text content such as AI responses, messages, and API responses. Use sign_bytes() or data_type="hash" for files, binary data, large content, or payloads you do not want to transmit directly.

Back to contents ↑