UUniCAS DOCS
Console GitHub
UNICAS / DOCUMENTATIONEdit source

#App-user SDK quickstarts

The repository quickstarts are tested from packed tarballs in a clean temporary directory. They do not resolve workspace source.

#Offline Node quickstart

This example creates one App-level client, injects a synthetic capability provider and no-network fetcher, and reads immutable metadata from one Space:

import assert from "node:assert/strict";
import { parseSpaceSelector } from "@unicas/space-protocol";
import { createAppCasClient } from "@unicas/space-client";

const hash = "a".repeat(64);
const spaceId = "/space-example";
const selector = parseSpaceSelector(spaceId);
if (!selector) throw new Error("Invalid example Space ID");
const cas = createAppCasClient({
  baseUrl: "https://api.example",
  appId: "app-example",
  capabilityProvider: {
    async acquire(requirement) {
      return {
        bearerToken: "synthetic-capability",
        metadata: {
          version: 2,
          expiresAt: Math.floor(Date.now() / 1000) + 300,
          grants: [{
            selector: selector.selector,
            permissions: [requirement.permission],
          }],
        },
      };
    },
  },
  fetcher: {
    fetch: async () => Response.json({
      metadata: { hash, size: 5, contentType: "text/plain", refs: [] },
    }),
  },
});

const metadata = await cas.readMetadata(spaceId, hash);
assert.equal(metadata.contentType, "text/plain");

Run the complete executable source:

pnpm exec tsx examples/app-user-sdk/node-quickstart.ts

#Browser cache quickstart

Attach a Principal-scoped cache to the transport, completely consume content before expecting it to be cached, and clear the Principal partition at logout:

import {
  clearBrowserCasNodeCaches,
  createBrowserCasNodeCache,
} from "@unicas/space-browser-cache";
import { parseSpaceSelector } from "@unicas/space-protocol";
import { createAppCasClient } from "@unicas/space-client";

const principal = "issuer.example:subject-example";
const spaceId = "/space-example";
const selector = parseSpaceSelector(spaceId);
if (!selector) throw new Error("Invalid example Space ID");
const cache = createBrowserCasNodeCache({
  namespace: { endpoint: "https://api.example", principal },
});
const cas = createAppCasClient({
  baseUrl: "https://api.example",
  appId: "app-example",
  capabilityProvider: {
    async acquire(requirement) {
      return {
        bearerToken: "synthetic-capability",
        metadata: {
          version: 2,
          expiresAt: Math.floor(Date.now() / 1000) + 300,
          grants: [{
            selector: selector.selector,
            permissions: [requirement.permission],
          }],
        },
      };
    },
  },
  cache,
});

await cas.readMetadata(spaceId, "a".repeat(64));
await clearBrowserCasNodeCaches({ principal });
cache.close();

The release gate bundles the complete browser-quickstart.ts and runs it in Chromium, Firefox, and WebKit.

#Production boundary

An App backend authenticates its user, authorizes the requested Principal and Space, and returns a short-lived capability plus its trusted metadata through an App-owned authenticated route. The provider metadata describes the opaque token to the SDK; it does not replace server authorization. Browser code must never contain the App signing key or administrator credentials.

Blob upload creates leases rather than a durable business reference. Persist the blob hash in App state first, then call retain with a stable request ID. For files, implement a Principal-scoped SpaceFileRootCatalog; commit() is the catalog-visible snapshot boundary.

The tested host-responsibilities.ts shows these sequencing rules.

#Optional live metadata read

The live-metadata.ts example reads caller-owned environment variables and performs one least-privileged metadata request. No public credential is provided or hosted. Use a non-production Space and follow the example README without committing the environment values.