Uhpenry
Security

Public Verification

How Snapgate, Snapcharge, and Snapshot work together to ensure secure access, verifiable purchases, and immutable project history.

Uhpenry's trust system is built on three interlocking components:

  • Snapgate: A secure, account-bound gateway for project access and moderation.
  • Snapcharge: A transactional record that captures the exact state of a project at the moment of purchase or access.
  • Snapshot: A persistent historical container storing all Snapgates and Snapcharges tied to a purchase.

Together, these components guarantee that:

  1. Access is tied to a single user account, no shared or anonymous downloads.
  2. All project data is cryptographically verifiable, even years later.
  3. Project versions are preserved exactly as they were at the time of access, down to the repository structure.

1. Snapgate: The Secure Entry Point

A Snapgate is created whenever a user attempts to access a project.
It is unique to that user and that project, meaning no other account can use the same Snapgate.

Core functions:

  • Access Control

    • The Snapgate is bound to the user's account.
    • Cannot be transferred or shared without violating the license.
  • Access Types

    • Download Access: Directly download the purchased version.
    • Invitation Access: Be added to a private repo or collaboration space.
  • License & Policy Agreement

    • Before creation, the user must accept the project license and booth terms.
    • Agreement is timestamped and stored for audit.
  • Owner Moderation Tools

    • Revoke access for license violations (e.g., redistribution).
    • Suspend access temporarily.
    • Record violations for dispute handling.
  • Geolocation Awareness

    • The Snapgate records where access attempts originate.
    • Abnormal patterns (e.g., user in Ghana, download from India) can trigger a review.

Example:
If a project states “No redistribution” and a user shares files online, the owner can revoke their Snapgate, cutting off further access.


2. Snapcharge: Transaction + State Preservation

A Snapcharge represents one complete access event inside a Snapgate.
This is not just a payment record, it's a time capsule of the project's state.

Multiple Snapcharges can exist for the same Snapgate when:

  • A project is updated to a new version.
  • The user upgrades/downgrades.
  • Additional access is granted (even if free).

Every Snapcharge records:

  • The exact repository structure.
  • The price (even if 0 for free updates).
  • The license and booth terms in effect at the time.
  • The commit or version tag.
  • Any refund or dispute policies.
  • Verification data, allowing anyone to confirm integrity.

Verification Process

  1. Integrity Signing

    • Project state is serialized into a deterministic JSON string.
    • Hashed and signed with Uhpenry's private key → produces an integrity hash and integrity token.
    • Tagged with a key ID (kid) for rotation tracking.
  2. Signer Verification

    • The integrity token + key ID are signed again by the “signer” layer.
    • This creates a circular verification (signer signs the integrity, integrity signs the data).
  3. Public Verification

    • Public keys are available so anyone can verify:
      • The data matches the original state at purchase.
      • No internal tampering has occurred.
    • The key rotation process is open-sourced for transparency.

3. Snapshot: The Immutable History

A Snapshot is the container of truth for a user's project history.
It stores:

  • User identity
  • Project owner identity
  • All Snapgates tied to that purchase
  • All Snapcharges from those Snapgates

Key rules:

  • Snapshots cannot be deleted, by design.
  • They are mutable only via new Snapcharges.
    There's no API or function to change them otherwise.
  • They maintain a full version history, so the project's exact state can be reconstructed.

Why This Design

This layered approach provides mutual protection:

  • Booths

    • Can revoke or suspend access quickly.
    • Can trace where downloads came from.
    • Have proof of terms acceptance.
  • Users

    • Can prove their purchase years later.
    • Can verify that their download hasn't been tampered with.
    • Have a transparent transaction log.
  • The Platform

    • Can act as a trust layer for both sides.
    • Can detect suspicious access patterns automatically.

Production Signing Code

'use node';

// convex/config/node/ed25529.ts

import { SnapchargeType } from '@/types';
import crypto, { webcrypto } from 'node:crypto';
import { JsonValueType, SARType } from '@/global/types';
import { JWK, SignJWT, jwtVerify, importJWK } from 'jose';
import {
  parseJSON,
  getTimestamp,
  errorResponse,
  getEnvVariable,
  stableStringify,
  successResponse,
} from '@/global/helpers';

import INTEGRITY_PUBLIC_KEY_V1 from '../../.well-known/uhp-integrity-20250809.public.json';
import SIGNER_PUBLIC_KEY_V1 from '../../.well-known/uhp-signer-20250809.public.json';

// Make sure Web Crypto API is available
// in this convex env
if (!globalThis.crypto) {
  // @ts-ignore
  globalThis.crypto = webcrypto;
}

export type VersionTypes = 'v1';

export type SignProofType = {
  key: JWK;
  kid: string;
  hash: string;
  identity: string;
};

export type GeneratedProof = {
  createdAt: number;
  reason: 'initial' | 'final' | 'update' | 'refund';
  signer: { token: string; hash: string; kid: string };
  integrity: { token: string; hash: string; kid: string };
};

// ===== CENSORED ENV VARS =====
// Keys are still parsed and typed, but values are hidden
export const Ed25519_SIGNER_KEY_V1 = parseJSON<JWK>(
  getEnvVariable('[REDACTED_SIGNER_KEY]')
);
export const Ed25519_INTEGRITY_KEY_V1 = parseJSON<JWK>(
  getEnvVariable('[REDACTED_INTEGRITY_KEY]')
);
export const Ed25519_KID_DATE_V1 = Number(
  getEnvVariable('[REDACTED_KID_DATE]')
);
export const CURRENT_VERSION = String(
  getEnvVariable('[REDACTED_CURRENT_VERSION]')
) as VersionTypes;
// =============================

export const UHPENRY_KID_DATES = { v1: Ed25519_KID_DATE_V1 };
export const UHPENRY_PRIVATE_KEYS = {
  v1: { integrity: Ed25519_INTEGRITY_KEY_V1, signer: Ed25519_SIGNER_KEY_V1 },
};
export const UHPENRY_PUBLIC_KEYS = {
  v1: { integrity: INTEGRITY_PUBLIC_KEY_V1, signer: SIGNER_PUBLIC_KEY_V1 },
};

/**
 * Creates a SHA-256 hash (hex) of the stable-stringified JSON data.
 *
 * @param data - The JSON value to hash.
 * @returns The hex-encoded SHA-256 hash string.
 */
export const hashDeterministicJson = (data: JsonValueType): string => {
  try {
    if (!data || typeof data !== 'object') {
      throw new Error('Invalid data for hashing. Expected a JSON object.');
    }
    const str = stableStringify(data);
    return crypto.createHash('sha256').update(str, 'utf8').digest('hex');
  } catch (error: any) {
    throw new Error(error?.message ?? 'Failed to hash JSON data');
  }
};

/**
 * Retrieves the Key ID (KID) and corresponding JWK keys for a given type and version.
 *
 * @param type - The type of key to retrieve ("integrity" or "signer").
 * @param version - Optional version identifier (defaults to CURRENT_VERSION).
 * @returns An object containing the KID string and the matching integrity/signer keys.
 * @throws If the version is unknown or the keys/KID are not found.
 */
export const getSignKID = (
  type: 'integrity' | 'signer',
  version?: VersionTypes
) => {
  try {
    const targetVersion = version ?? CURRENT_VERSION;

    // Validate type
    if (type !== 'integrity' && type !== 'signer') {
      throw new Error(
        `Invalid type: ${type}. Expected "integrity" or "signer".`
      );
    }

    // Validate keys object
    const keys = UHPENRY_PRIVATE_KEYS[targetVersion];
    if (!keys || typeof keys !== 'object') {
      throw new Error(`No valid keys found for version: ${targetVersion}`);
    }

    // Make sure required JWK properties exist
    const { kty, crv, x, d } = keys[type];
    if (kty !== 'OKP' || crv !== 'Ed25519') {
      throw new Error(
        `Invalid JWK type/curve for version ${targetVersion}. Expected OKP+Ed25519.`
      );
    }
    if (!x) {
      throw new Error(
        `Missing "x" (public key) in keys for version ${targetVersion}.`
      );
    }
    if (!d) {
      throw new Error(
        `Missing "d" (private key) in keys for version ${targetVersion}.`
      );
    }

    // Validate KID date mapping
    const kidDate = String(UHPENRY_KID_DATES[targetVersion]);
    if (!kidDate || kidDate.trim() === '') {
      throw new Error(`No KID date found for version: ${targetVersion}`);
    }

    // Build KID
    const kid = `uhp-${type}-${kidDate}.public.json`;
    return { kid, keys };
  } catch (error: any) {
    throw new Error(error?.message ?? 'Failed to get signing KID');
  }
};

/**
 * Creates a cryptographically signed proof token (JWT) for any Uhpenry entity.
 *
 * This proof can be used to verify the authenticity and integrity of data such as:
 * - Snapcharge transactions
 * - License files (PDF, etc.)
 * - Snapshots or other signed assets
 *
 * The generated token is signed using the provided JWK and includes:
 * - `hash`: A deterministic hash of the signed data
 * - `sub` (subject): An identity string that uniquely identifies the entity (e.g., transaction ID)
 * - `aud` (audience): Always set to 'uhpenry.com' for public verification context
 * - `iss` (issuer): Always set to 'uhpenry.com' to indicate the token origin
 *
 * @typedef {Object} SignProofType
 * @property {JWK} key       - JSON Web Key used to sign the token.
 * @property {string} kid    - Key ID for identifying the signing key.
 * @property {string} hash   - Deterministic hash of the entity's data.
 * @property {string} identity - Unique identifier for the entity this proof is about.
 *
 * @param {SignProofType} args - Signing arguments including key, kid, hash, and entity identity.
 * @returns {Promise<SARType<{ token: string; hash: string } | null>>}
 * Returns a success response with the signed token and hash, or an error response.
 */
export const signProof = async (
  args: SignProofType
): Promise<SARType<{ token: string; hash: string } | null>> => {
  try {
    const { hash, key, identity, kid } = args;

    if (!key || typeof key !== 'object') {
      throw new Error('Signing key is missing or not an object');
    }

    const { kty, crv, x, d } = key as Partial<JWK>;

    if (kty !== 'OKP' || crv !== 'Ed25519') {
      throw new Error(
        `Invalid JWK type/curve. Expected OKP+Ed25519, got kty=${kty} crv=${crv}`
      );
    }

    if (!x) throw new Error('JWK missing public parameter "x"');
    if (!d)
      throw new Error(
        'JWK missing private parameter "d" (private key required for signing)'
      );

    const privateKey = await importJWK(key, 'EdDSA');

    // Ensure it's private key
    if ((privateKey as any).type !== 'private') {
      throw new Error('Imported key is not a private key');
    }

    const token = await new SignJWT({ hash })
      .setProtectedHeader({ alg: 'EdDSA', kid })
      .setIssuedAt()
      .setIssuer('uhpenry.com')
      .setSubject(identity)
      .setAudience('uhpenry.com')
      .sign(privateKey);

    return successResponse('Proof signed successfully', { token, hash });
  } catch (error: any) {
    console.log('checkpoint 5.4', {
      message: error?.message,
      stack: error?.stack,
    });
    return errorResponse(error.message ?? 'Failed to sign proof', null);
  }
};

/**
 * Generates a two-stage cryptographic proof (integrity + signer) for a Snapcharge object.
 *
 * @param {SnapchargeType} data - The original JSON object to sign.
 * @param {'initial' | 'final' | 'update' | 'refund'} reason - Reason for creating this proof.
 * @returns {Promise<GeneratedProof>} - The integrity proof, signer proof, creation timestamp, and reason.
 *
 * @throws {Error} If signing fails for either the integrity or signer stage.
 */
export const generateSignedProof = async (
  Snapcharge: SnapchargeType,
  reason: 'initial' | 'final' | 'update' | 'refund'
): Promise<GeneratedProof> => {
  try {
    if (!Snapcharge || typeof Snapcharge !== 'object') {
      throw new Error(`Invalid Snapcharge: must be a non-null object`);
    }
    if (!Snapcharge._id) {
      throw new Error(`Invalid Snapcharge: missing or invalid "_id"`);
    }

    if (!['initial', 'final', 'refund'].includes(reason)) {
      throw new Error(`Invalid reason: ${reason}`);
    }

    if (Snapcharge?.verifications) {
      throw new Error(
        'Verifications field should be removed from Snapcharge when generating signed proof'
      );
    }

    // Get deterministic hash of the Snapcharge data (hash A)
    let hash: string;
    try {
      hash = hashDeterministicJson(Snapcharge);
    } catch (err: any) {
      throw new Error(`Failed to hash Snapcharge: ${err?.message ?? err}`);
    }
    if (typeof hash !== 'string' || !hash.trim()) {
      throw new Error(`Generated hash for Snapcharge is invalid`);
    }

    // Retrieve keys for integrity and signer
    const { keys: integrityKeys, kid: integrityKid } = getSignKID('integrity');
    const { keys: signerKeys, kid: signerKid } = getSignKID('signer');

    if (
      !integrityKeys?.integrity ||
      typeof integrityKeys.integrity !== 'object'
    ) {
      throw new Error(`Missing or invalid integrity private key`);
    }
    if (!signerKeys?.signer || typeof signerKeys.signer !== 'object') {
      throw new Error(`Missing or invalid signer private key`);
    }

    // Sign hash A with the integrity key
    const integrityParams = {
      hash,
      identity: Snapcharge._id,
      key: integrityKeys.integrity,
      kid: integrityKid,
    };
    const integrityResponse = await signProof(integrityParams);
    if (!integrityResponse?.success || !integrityResponse.payload) {
      throw new Error(
        `Integrity signing failed: ${
          integrityResponse?.message ?? 'unknown error'
        }`
      );
    }
    if (!integrityResponse.payload.token || !integrityResponse.payload.hash) {
      throw new Error(`Integrity signing returned incomplete payload`);
    }

    const integrity = {
      ...integrityResponse.payload,
      kid: integrityKid,
    };

    // Get deterministic hash of the integrity object (hash B)
    let integrityHash: string;
    try {
      integrityHash = hashDeterministicJson(integrity);
    } catch (err: any) {
      throw new Error(`Failed to hash integrity proof: ${err?.message ?? err}`);
    }
    if (typeof integrityHash !== 'string' || !integrityHash.trim()) {
      throw new Error(`Generated hash for integrity proof is invalid`);
    }

    // Sign hash B with the signer key
    const signerParams = {
      hash: integrityHash,
      identity: Snapcharge._id,
      key: signerKeys.signer,
      kid: signerKid,
    };
    const signerResponse = await signProof(signerParams);
    if (!signerResponse?.success || !signerResponse.payload) {
      throw new Error(
        `Signer signing failed: ${signerResponse?.message ?? 'unknown error'}`
      );
    }
    if (!signerResponse.payload.token || !signerResponse.payload.hash) {
      throw new Error(`Signer signing returned incomplete payload`);
    }

    const signer = {
      ...signerResponse.payload,
      kid: signerKid,
    };

    // Return the proofs along with metadata
    return {
      integrity,
      signer,
      createdAt: getTimestamp(),
      reason,
    };
  } catch (error: any) {
    throw new Error(error?.message ?? 'Failed to generate signed proof');
  }
};

/**
 * Retrieves the public keys object based on a given Key ID (kid).
 *
 * Expected kid format: 'uhp-{integrity|signer}-{yyyymmdd}.public.json'
 *
 * @param {string} kid - The Key ID string from the token header.
 * @returns {Record<'integrity' | 'signer', JWK>} - The corresponding public keys for the version.
 * @throws Will throw an error if the version or keys are not found.
 */
export const getPublicKey = (kid: string): JWK => {
  try {
    if (typeof kid !== 'string' || !kid.trim()) {
      throw new Error(`Invalid KID: must be a non-empty string`);
    }

    // Ensure .public.json suffix
    if (!kid.endsWith('.public.json')) {
      throw new Error(
        `Invalid kid format (missing .public.json suffix): ${kid}`
      );
    }

    // Extract the main part before '.public.json'
    const mainPart = kid.split('.').shift();
    if (!mainPart) {
      throw new Error(`Invalid kid format (empty main part): ${kid}`);
    }

    // Expected format: 'uhp-{type}-{date}'
    const parts = mainPart.split('-');
    if (parts.length !== 3 || parts[0] !== 'uhp') {
      throw new Error(`Unexpected kid structure: ${kid}`);
    }

    const [, type, dateStr] = parts;
    const date = Number(dateStr);

    if (type !== 'integrity' && type !== 'signer') {
      throw new Error(`Invalid key type in kid: ${type}`);
    }
    if (!Number.isInteger(date) || date <= 0) {
      throw new Error(`Invalid date in kid: ${dateStr}`);
    }

    // Find version matching the date from UHPENRY_KID_DATES
    const versionEntry = Object.entries(UHPENRY_KID_DATES).find(
      ([, dateValue]) => dateValue === date
    ) as [VersionTypes, number] | undefined;

    if (!versionEntry) {
      throw new Error(`No version found for date: ${date}`);
    }

    const [version] = versionEntry;

    if (!UHPENRY_PUBLIC_KEYS[version]) {
      throw new Error(`No public keys object found for version: ${version}`);
    }

    const publicKey = UHPENRY_PUBLIC_KEYS[version][type] as JWK;

    if (!publicKey || typeof publicKey !== 'object') {
      throw new Error(
        `No public key found for type "${type}" in version "${version}"`
      );
    }

    // Ensure JWK shape is valid for Ed25519
    const { kty, crv, x } = publicKey as Partial<JWK>;
    if (kty !== 'OKP' || crv !== 'Ed25519') {
      throw new Error(
        `Invalid JWK type/curve for version ${version}. Expected OKP+Ed25519.`
      );
    }

    if (!x) {
      throw new Error(`Public JWK missing "x" parameter in version ${version}`);
    }

    return publicKey;
  } catch (error: any) {
    throw new Error(error?.message ?? 'Failed to get public key from KID');
  }
};

/**
 * Verifies the signed proof object.
 * Throws error if verification fails.
 *
 * @param proof - The proof object generated by generateSignedProof
 * @param originalData - The original Snapcharge JSON data
 * @returns {Promise<SARType<boolean>>} - true if verification succeeds
 */
export const verifySignedProof = async (
  proof: GeneratedProof,
  originalData: SnapchargeType
): Promise<SARType<boolean>> => {
  try {
    // Verify signer token first using public key
    const signerKid = proof.signer.kid;
    const signerPublicKeyJwk = getPublicKey(signerKid);

    const signerKey = await importJWK(signerPublicKeyJwk);

    const signOption = {
      issuer: 'uhpenry.com',
      audience: 'uhpenry.com',
      algorithms: ['EdDSA'],
      requiredClaims: ['sub', 'aud', 'iss'],
      subject: originalData._id,
    };

    const { payload: signerPayload } = await jwtVerify(
      proof.signer.token,
      signerKey,
      signOption
    );

    // The signer payload contains the integrity hash
    const signerHash = signerPayload.hash as string;

    // Verify integrity token using public key
    const integrityKid = proof.integrity.kid;
    const integrityPublicKeyJwk = getPublicKey(integrityKid);
    const integrityKey = await importJWK(integrityPublicKeyJwk);

    const { payload: integrityPayload } = await jwtVerify(
      proof.integrity.token,
      integrityKey,
      signOption
    );

    // The integrity payload contains the original data hash
    const integrityHash = integrityPayload.hash as string;

    // Recalculate original data hash and compare with integrity hash
    const recalculatedOriginalHash = hashDeterministicJson(originalData);
    if (recalculatedOriginalHash !== integrityHash) {
      throw new Error('Original data hash does not match integrity hash');
    }

    // Recalculate integrity object hash and compare with signer hash
    const recalculatedIntegrityHash = hashDeterministicJson(proof.integrity);

    if (recalculatedIntegrityHash !== signerHash) {
      throw new Error('Integrity object hash does not match signer hash');
    }

    return successResponse('Signed proof verified successfully', true);
  } catch (error: any) {
    return errorResponse(
      error.message ?? 'Failed to verify signed proof',
      false
    );
  }
};

/**
 * Verifies the most recent proof for a given Snapcharge transaction.
 *
 * This function extracts the latest verification record from the provided `Snapcharge` object
 * and validates it using `verifySignedProof`. It ensures that:
 * - At least one verification record exists.
 * - The latest verification record is not an "initial" verification.
 * - The signed proof is successfully verified.
 *
 * @param {SnapchargeType} Snapcharge - The Snapcharge object containing transaction data and verifications.
 *
 * @returns {Promise<SARType<boolean>}
 * Returns a `successResponse` if verification passes, otherwise an `errorResponse` with the failure reason.
 *
 * @throws {Error} If there are no verifications, the latest proof is invalid,
 * or verification fails at any stage.
 */
export const verifySnapchargeProof = async (
  Snapcharge: SnapchargeType
): Promise<SARType<boolean>> => {
  try {
    const { verifications = [], ...SnapchargeJson } = Snapcharge;
    if (verifications.length === 0) {
      throw new Error('No verification data available.');
    }

    const proof = verifications.at(-1);
    if (!proof) {
      throw new Error('No valid verification record found.');
    }

    if (proof.reason === 'initial') {
      throw new Error(
        'Initial verification is not valid for this action. Please wait until payment and fee processing are complete, then try again.'
      );
    }

    const response = await verifySignedProof(proof, SnapchargeJson);
    if (!response.success || !response.payload) {
      throw new Error(response.message || 'Verification failed.');
    }

    return successResponse(response.message ?? '', true);
  } catch (error: any) {
    return errorResponse(error?.message ?? '', false);
  }
};

Public Key for Verification

You can always fetch the latest public key from either of these official sources:

  1. Open-Source Key Signing: latest active key and rotation history: github.com/uhpenry/key-signing

  2. JWKS (JSON Web Key Set) endpoint: contains all active public keys, their versions, and metadata for automated verification: JWKS


Key Rotation

  • We rotate signing keys every 6 months as part of our security protocol.
  • The JWKS endpoint contains all currently valid keys with kid (Key ID) and validity intervals so you can determine which key to use for verification at any given time.
  • The GitHub repository also contains historical keys for verifying Snapcharges that were signed before a rotation event.

By following the JWKS data or the GitHub key history, you can verify any Snapcharge ever issued by Uhpenry.

Roadmap: Blockchain Anchoring

Currently, Snapgates, Snapcharges, and Snapshots are secured cryptographically without a blockchain.
However, a blockchain anchor is planned:

  • Each update would be stored as a block.
  • Blocks represent project versions over time.
  • Public auditability: Anyone could verify the full history independently.

Summary Diagram

Loading diagram...