#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.