Files
ocas/README.md
T
xiaoju a62e18a189 docs: unify alias→variable, refactor var commands
- All docs/cards: alias → variable name
- CLAUDE.md: add Architecture Notes section
- README: fix --store→--home, add variable names section
- CLI: var commands use openStoreAndVarStore(), no double bootstrap
- Tests updated for bootstrap-seeded variables

Fixes #20
2026-06-01 12:38:25 +00:00

6.3 KiB

OCAS

Object Content Addressable Store — self-describing CAS with JSON Schema typed nodes.

Overview

OCAS is a monorepo for storing and validating JSON data in a content-addressable store (CAS). Each node has a typed payload: its type field is the hash of a JSON Schema node that describes the payload shape. Hashes are 13-character Crockford Base32 strings derived from XXH64 over deterministic CBOR encoding.

A bootstrap meta-schema is stored as a self-referencing seed node (type === hash). All other schemas are registered as nodes typed by that meta-schema. Payloads can reference other nodes via format: "cas_ref" fields; the library provides traversal, reference extraction, and integrity verification.

Use the in-memory store for tests and embedded apps, the filesystem store for persistence, and the CLI for local store management.

Architecture

  ┌───────────┐
  │ @ocas/cli │
  └─────┬─────┘
        │
        ▼
  ┌───────────┐
  │ @ocas/fs  │
  └─────┬─────┘
        │
        ▼
  ┌────────────┐
  │ @ocas/core │
  └────────────┘
Layer Package Role
Core @ocas/core Hashing, schemas, stores, verify, bootstrap
Storage @ocas/fs Filesystem-backed Store
CLI @ocas/cli ocas command-line tool

Packages

Package Description Type
@ocas/core Core CAS engine — hashing, schema, store, verify, bootstrap lib
@ocas/fs Filesystem-backed CAS store lib
@ocas/cli CLI tool (ocas binary) cli

Quick Start

git clone <repo-url>
cd ocas
bun install --no-cache
bun run build
import {
  bootstrap,
  createMemoryStore,
  putSchema,
  validate,
} from "@ocas/core";

const store = createMemoryStore();
await bootstrap(store);

const typeHash = await putSchema(store, {
  type: "object",
  properties: { message: { type: "string" } },
  required: ["message"],
  additionalProperties: false,
});

const hash = await store.put(typeHash, { message: "hello" });
const node = store.get(hash);
console.log(validate(store, node!)); // true

For a persistent store:

import { createFsStore } from "@ocas/fs";
import { bootstrap } from "@ocas/core";

const store = createFsStore("/path/to/store");
await bootstrap(store);

Or use the CLI (see CLI Reference and packages/cli/README.md).

CLI Reference

Binary: ocas (from @ocas/cli). Default store: ~/.ocas.

The store is auto-created and bootstrapped on first use — there is no init/bootstrap command, and schemas are ordinary @schema-typed nodes (ocas put @schema file.json), so there is no schema subcommand.

Envelope format

Every JSON-emitting command prints a uniform { type, value } envelope. type is the hash of the command's @output/* result schema and value is the command payload. This makes output self-describing and pipeable: feed any envelope into render -p to render its value (embedded cas_ref hashes are expanded). render is the only command that emits raw (non-envelope) text.

// ocas has <hash>
{ "type": "AYHQD2YA9G667", "value": true }
Usage: ocas [--home <path>] [--json] <command> [args]

Commands (all emit a { type, value } envelope unless noted):
  put <type-hash> <file.json>       Store node (value = hash)          (@output/put)
  get <hash>                        Node payload + metadata            (@output/get)
  has <hash>                        Existence boolean                  (@output/has)
  verify <hash>                     ok / corrupted / invalid           (@output/verify)
  refs <hash>                       Direct cas_ref edges               (@output/refs)
  walk <hash> [--format tree]       Recursive traversal                (@output/walk)
  hash <type-hash> <file.json>      Compute hash without storing       (@output/hash)
  render <hash> [options]           Render node as text (raw output)
  render --pipe/-p [options]        Render a piped envelope (raw output)
  list --type <hash-or-name>        Hashes for a type (value = list)   (@output/list)
  list-meta                         Meta-schema hashes                 (@output/list-meta)
  list-schema                       All schema hashes                  (@output/list-schema)
  var set|get|delete|tag|list ...   Variable CRUD                      (@output/var-*)
  template set|get|list|delete ...  Output-template CRUD               (@output/template-*)
  gc                                Garbage collection                 (@output/gc)

Flags:
  --home <path>   Store directory (default: $OCAS_HOME or ~/.ocas)
  --json          Compact JSON output
  --pipe, -p      Read a { type, value } envelope from stdin for render

Variable names

Any command that takes a hash also accepts a variable name. Builtin schemas (@ocas/schema, @ocas/string, @ocas/object, @ocas/output/*, …) are registered in the variable store during bootstrap; user variables created via ocas var set <name> <hash> resolve the same way. There is no separate alias concept — every name lookup queries the variable store.

Pipe examples

# Store a node, then render the stored content (the put envelope's hash is
# a cas_ref, so render -p dereferences and renders it):
ocas put @schema ./schemas/item.json | ocas render -p

# Render garbage-collection stats:
ocas gc | ocas render -p

# List every schema, then consume the envelope's value array with jq:
ocas list --type @schema | jq -r '.value[]'

Development

bun install --no-cache   # install workspace dependencies
bun run build            # tsc --build (libs)
bun run check            # biome check
bun run format           # biome format --write
bun test                 # run all package tests

Publishing

Releases use Changesets. From the repo root:

bun run release   # changeset version → build → publish to npm (@ocas/*)

Individual packages block prepublishOnly and expect releases via the workspace release script.

License

MIT