# Omnorium Documentation

Omnorium is a portfolio monitor for GitHub repositories and coding agent sessions across Claude Code, Codex, Seorak, and Salidium.

Source: https://omnorium.com/#docs

# Omnorium Local Setup & Catalog Access (Claude Code / Codex)

Follow these steps to start or connect to a local Omnorium instance and read `/consumer/v1/catalog` (`omnorium.catalog` 1.0).

## 1. Detect or Start the Local Server
Check ~/.omnorium/consumer.json first. If missing, start the server in the background from the Omnorium repository root (Node.js >= 22.12) and wait for ~/.omnorium/consumer.json to appear.

```sh
# Live GitHub mode (ask user to sign in at http://127.0.0.1:4411 if no account exists):
pnpm install && pnpm dev

# Or 38-project sample dataset (no GitHub account required):
pnpm install && pnpm dev:sample
```

## 2. Read Discovery Metadata & Verify Instance
Read ~/.omnorium/consumer.json (mode 0600) to obtain baseUrl (default http://127.0.0.1:4410/consumer/v1), sample (true/false), instanceId, and secret (32-byte base64url key for GET /consumer/v1/discovery?challenge=<32-byte-base64url>).

```sh
CHALLENGE=$(node -e "process.stdout.write(require('crypto').randomBytes(32).toString('base64url'))")
curl -fsSL "http://127.0.0.1:4410/consumer/v1/discovery?challenge=$CHALLENGE"
```

## 3. Issue an Idempotent Read-Only Credential & Read Catalog
Never print or log the raw omc_ token. Match OMNORIUM_SAMPLE=small when sample is true in consumer.json, rotate the coding-agent label idempotently, and query GET /consumer/v1/catalog (contract omnorium.catalog 1.0) without Origin or Sec-Fetch-Site headers.

```sh
if grep -q '"sample":true' ~/.omnorium/consumer.json 2>/dev/null; then
  OMNORIUM_SAMPLE=small pnpm -s consumer:revoke coding-agent >/dev/null 2>&1 || true
  TOKEN=$(OMNORIUM_SAMPLE=small pnpm -s consumer:create --label coding-agent)
else
  pnpm -s consumer:revoke coding-agent >/dev/null 2>&1 || true
  TOKEN=$(pnpm -s consumer:create --label coding-agent)
fi
curl -fsSL -H "Authorization: Bearer $TOKEN" http://127.0.0.1:4410/consumer/v1/catalog
```

---

## 1. Quickstart: Run locally or on Cloudflare Workers.

Start the local server with Node.js 22.12+, explore the 38-project sample dataset without GitHub, or deploy to Cloudflare Workers and D1.

Source: https://omnorium.com/#docs/quickstart

## Run Modes

### Local Server

```sh
pnpm install
pnpm dev

# Open http://127.0.0.1:4411 and select Connect GitHub
```

### Sample Data

```sh
# Start with the 38-project sample collection (no GitHub required)
pnpm dev:sample

# Start with the 400-project sample collection
pnpm dev:sample:large
```

### Cloudflare Workers

```sh
# Deploy to Cloudflare Workers with a D1 SQLite database
pnpm deploy:workers \
  --url https://omnorium.example.com \
  --account <cloudflare-account-id>
```

## Three Steps

- **01 Start Server**: Run pnpm dev on port 4411 for a local SQLite database, or deploy to Cloudflare Workers and D1.
- **02 Connect GitHub**: Confirm the pre-configured app name on GitHub. Omnorium creates its read-only GitHub App and signs in with no secrets to copy.
- **03 Select Repositories**: Choose repositories across personal accounts and organizations. Related repositories are suggested as one project.

## Optional Settings

Local development requires no configuration. Set these variables for custom origins or ports.

| Variable | Default | Purpose |
| --- | --- | --- |
| OMNORIUM_PUBLIC_URL | http://127.0.0.1:4411 | Address where the web application is served |
| OMNORIUM_SECRET_KEY | .data/instance.key | Key for encrypting credentials at rest (required on public hosts) |
| OMNORIUM_SETUP_TOKEN | None | Random value of at least 32 characters required for remote setup |
| OMNORIUM_API_PORT / WEB_PORT | 4410 / 4411 | Development ports for the API server and web application |
| OMNORIUM_HOME | ~/.omnorium | Directory where the Node server writes consumer.json |

---

## 2. GitHub App: Read-only GitHub permissions and activity sync.

Omnorium creates its GitHub App from the browser and requests read-only access to repository metadata, pull requests, checks, and commit statuses. Source code is never read.

Source: https://omnorium.com/#docs/github-app

## Permissions

The GitHub App requests four read-only permissions and never requests repository contents.

| Permission | Access | Data Read |
| --- | --- | --- |
| Metadata | Read | Repository names, visibility, primary language, forks, and archived status |
| Pull requests | Read | Open pull requests, draft state, review decisions, and requested reviewers |
| Checks | Read | Check runs and conclusions on the default branch and open pull requests |
| Commit statuses | Read | Combined status contexts on the default branch and open pull requests |
| Contents | Not Requested | Source code files, commits, and trees are never read |

## Setup Link

On loopback addresses (127.0.0.1), setup starts in the browser. On public HTTPS hosts, a one-time setup link authorizes GitHub App creation.

### Remote Setup Link

```sh
# First Cloudflare Workers deploy prints the setup link once:
pnpm deploy:workers --url https://omnorium.example.com --account <id>

# Open the setup link in the browser:
https://omnorium.example.com/#setup=<OMNORIUM_SETUP_TOKEN>
```

### Webhook Delivery

```sh
Endpoint:   POST /api/github/webhook
Signature:  X-Hub-Signature-256 (HMAC-SHA256 verified in constant time)
Events:     pull_request, pull_request_review, check_suite, status, installation
```

## Activity Sync

- **WEBHOOKS Signed Events**: Public HTTPS instances register a webhook that receives pull request, review, check, and status updates from GitHub.
- **RECONCILE 15-Minute Schedule**: A 15-minute scheduled job re-reads up to 10 linked repositories per run, least recently read first.
- **TEAM PRS Team Reviews**: Finds open pull requests requesting review from a GitHub team without asking for organization Members permission.

---

## 3. Map and List: One portfolio across the Map and the List.

Omnorium gathers linked repositories into one collection and derives one state per project in precedence order.

Source: https://omnorium.com/#docs/map-and-list

## System Objects

Every view and Consumer API response uses the same nine system objects.

| Object | Scope | Definition |
| --- | --- | --- |
| Account | GitHub Owner | A GitHub user or organization with the app installed |
| Group | Account Column | A user-defined set of projects inside one Account, drawn on Grass or Water ground |
| Project | Product Unit | One product holding one or more repositories, with one primary repository |
| Repository | GitHub Source | A GitHub repository supplying metadata, pull requests, and check results |
| Agent Session | Signal Source | One Claude Code or Codex run reported for a linked repository |
| Avatar | Map Species | The species that renders a project on the Map: Sheep, Duck, Fox, or Rabbit |
| Map / List / Inbox | Views | Spatial view (Map), tabular view (List), and ranked actionable column (Inbox) |

## Project States

When several signals apply to a project, Omnorium assigns the highest-precedence state below.

| Rank | State Badge | Inbox Section | Derivation Rule |
| --- | --- | --- | --- |
| 01 | Awaiting Input | Action Required | Signal source reports an active Claude Code or Codex session blocked on the user |
| 02 | Session Failed | Action Required | Signal source reports an agent session ended in an error, until viewed |
| 03 | Checks Failing | Action Required | Latest completed check run or status on the default branch failed or timed out |
| 04 | Review Requested | Action Required | User or a team they belong to is a requested reviewer on an open PR |
| 05 | Changes Requested | Action Required | Latest review on the user's open PR requests changes |
| 06 | Ready to Merge | Action Required | User's open PR is approved with passing checks and is not a draft |
| 07 | Agent Running | Agents Running | One or more agent sessions are active within the liveness window |
| 08 | Session Complete | Recent Activity | Agent session ended within 24 hours and has not yet been viewed |
| 09 | Active | Recent Activity | Activity recorded within the configured Inactive After window (2 weeks, 30, 60, or 90 days) |
| 10 | Inactive | Inactive | No activity within the configured Inactive After window |
| 11 | Archived | Archived | Primary repository is archived on GitHub |
| 12 | Access Lost | Coverage | API returns 404 or the repository left the installation |

## Keyboard Shortcuts

Move between projects, switch views, and filter the collection from the keyboard.

| Keys | Surface | Action |
| --- | --- | --- |
| / or ⌘K | Global | Open Search and the command palette |
| 1 | Global | Move focus to the Map or the List |
| 2 | Global | Switch between the Map and the List |
| Enter / Space | Map and List | Open the focused project card (Enter) or toggle selection (Space) |
| F, +, -, 0 | Map | Fit all projects in view (F), zoom in (+), zoom out (-), or reset zoom (0) |
| org:  lang:  group: | Search | Narrow projects by Account login, primary language, or Group name |

---

## 4. Agent Telemetry: Connect Claude Code and Codex through Seorak and Salidium.

Omnorium reads live agent sessions server-to-server from the instance owner's Seorak cell and opens Salidium for the session list.

Source: https://omnorium.com/#docs/agent-telemetry

## Signal Sources

- **01 Claude Code & Codex**: Coding agents work in local repository checkouts and report session events to Seorak.
- **02 Seorak Cell**: The Omnorium server reads live sessions from the configured Seorak cell with a sealed srkx_ credential.
- **03 Salidium List**: Open Session List on a project card opens Salidium and marks ended sessions viewed.

## Configuration

Set the Seorak cell origin, the owner's GitHub user ID, and the optional Salidium address at startup or deploy.

### Local Server

```sh
export OMNORIUM_SEORAK_ORIGIN="https://cell.seorak.app"
export OMNORIUM_SEORAK_OWNER_ID="12345678"
export OMNORIUM_SALIDIUM_URL="http://127.0.0.1:47822"

pnpm dev
```

### Cloudflare Workers

```sh
pnpm deploy:workers \
  --url https://omnorium.example.com \
  --account <cloudflare-account-id> \
  --seorak-origin https://cell.seorak.app \
  --seorak-owner <github-user-id> \
  --salidium-url http://127.0.0.1:47822
```

### Connect Seorak

```sh
1. Issue a Seorak credential starting with srkx_ with Live sessions and Repository names scopes.
2. Turn on repository names under Capture in Seorak Settings.
3. In Omnorium Settings, enter the Credential and select Connect Seorak.
```

## Coverage States

Settings and the Inbox foot show the state of the Seorak connection.

| Badge | Condition | Detail |
| --- | --- | --- |
| Connected | Read succeeded within 3 minutes | Live sessions are current across the Map, the List, and the Consumer API. |
| Not Connected | No credential or access ended | Agent session telemetry requires Seorak. Connect Seorak in Settings to show running and waiting sessions. |
| Permission Required | Missing scope or repository names off | Agent sessions require the Live sessions and Repository names scopes. Issue a Seorak credential with both, then connect it in Settings. |
| Sync Paused | Seorak unreachable | Seorak is unreachable. Agent sessions return after the next successful read. |
| Rate Limited | Seorak rate limit reached | Seorak rate limit reached. Shows the next read time when Seorak provides one. |

---

## 5. Consumer API: Read the portfolio catalog from local programs.

The local Node server serves the read-only omnorium.catalog 1.0 contract at /consumer/v1 with HMAC-SHA256 instance discovery and omc_ bearer credentials.

Source: https://omnorium.com/#docs/consumer-api

## Issue Credential

Create a read-only consumer credential for a local script or coding agent and read /consumer/v1/catalog.

### CLI and cURL

```sh
# 1. Create a consumer credential (token is printed once)
TOKEN=$(pnpm -s consumer:create --label desk-cli)

# 2. Read the portfolio catalog
curl -sH "Authorization: Bearer $TOKEN" \
  http://127.0.0.1:4410/consumer/v1/catalog | jq .

# 3. List or revoke credentials
pnpm consumer:list
pnpm consumer:revoke desk-cli
```

### TypeScript Discovery

```sh
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
import { readFile } from 'node:fs/promises';

const raw = await readFile(`${process.env.HOME}/.omnorium/consumer.json`, 'utf8');
const disc = JSON.parse(raw);
const challenge = randomBytes(32);
const baseUrl = disc.contracts[0].baseUrl;

const res = await fetch(`${baseUrl}/discovery?challenge=${challenge.toString('base64url')}`);
const { instanceId, proof } = await res.json();
const expected = createHmac('sha256', Buffer.from(disc.secret, 'base64url')).update(challenge).digest();
if (instanceId !== disc.instanceId || !timingSafeEqual(expected, Buffer.from(proof, 'base64url'))) {
  throw new Error('Discovery proof mismatch');
}

const catalog = await fetch(`${baseUrl}/catalog`, {
  headers: { Authorization: `Bearer ${process.env.OMNORIUM_TOKEN}` }
}).then(r => r.json());
```

### Catalog Response

```sh
{
  "apiVersion": "1.0",
  "instanceId": "inst_01j8x9k2",
  "generatedAt": "2026-10-31T16:40:00.000Z",
  "sample": false,
  "testGitHub": false,
  "user": { "login": "alex", "name": "Alex Rivera" },
  "sync": { "repositoriesAt": "2026-10-31T16:35:00.000Z", "repositoriesError": null, "activityAt": "2026-10-31T16:38:00.000Z", "activityError": null },
  "accounts": [
    { "login": "lumen-works", "type": "Organization", "permissions": { "pullRequests": "read", "checks": "read", "statuses": "read" } }
  ],
  "groups": [
    { "id": "grp_client", "name": "Client Work", "order": 0 }
  ],
  "projects": [
    {
      "id": "prj_atlas",
      "name": "Atlas",
      "nameIsOverride": false,
      "groupId": "grp_client",
      "groupOrder": 0,
      "state": {
        "state": "checks_failing",
        "states": ["checks_failing", "active"],
        "reason": { "kind": "checks", "repoId": 101, "at": "2026-10-31T14:40:00.000Z" },
        "active": true,
        "lastActivityAt": "2026-10-31T14:40:00.000Z",
        "sessions": { "running": 0, "awaitingInput": 0 },
        "unknown": []
      },
      "attention": [
        { "kind": "checks_failing", "repositoryId": 101, "pullRequest": null, "at": "2026-10-31T14:40:00.000Z" }
      ],
      "updatedAt": "2026-10-31T14:40:00.000Z"
    }
  ],
  "truncated": null
}
```

## Endpoints

The Consumer API runs on the local Node server only and refuses cross-origin browser requests.

| Route / File | Access | Behavior |
| --- | --- | --- |
| ~/.omnorium/consumer.json | Mode 0600 (dir 0700) | Written on start with instanceId, 32-byte secret, and baseUrl; removed on clean stop |
| GET /consumer/v1/discovery | Challenge query | Returns instanceId, contracts list, and HMAC-SHA256(challenge, secret) proof |
| GET /consumer/v1/catalog | Bearer omc_... | Returns every project, its state, and up to 5 attention items; supports ETag (304) |
| Origin Guard | 403 origin_not_allowed | Refuses requests with an Origin or Sec-Fetch-Site header |
| Rate Limit | 429 / 503 not_ready | Limits refused token attempts to 30 per minute; returns 503 before the first sync completes |
