Titentiten.devꦠꦶꦠꦺꦤ꧀
v0.1.1 · npmGitHub
DocsCollaborationIdentity & visibility

Identity & visibility

Six scopes narrow every record, three visibility levels decide who can retrieve it, and one rule outranks both — authority comes from the credential, never from the request body.

34 scopesprivate · team · organizationtrust ceiling per key

Six identities, not one “user”

A store that collapses everyone into a user_id cannot answer the two questions that matter once several agents share it: who said this, and who is it about?

ROLES THE MODEL DISTINGUISHES

actor the identity performing the operation — always the credential's principal

subject the person, system or entity the memory concerns

observer the identity whose perspective a claim represents

agent a software identity acting under a human or team policy

service a non-agent automation identity

organization the tenant boundary, plus workspace and project as collaboration scopes

An agent gets its own revocable credential. A credential shared between two agents is not a membership — it is one principal with two operators, and the private-memory boundary below is drawn around the principal.

The scope ladder

Six dimensions narrow a record. They are not all set the same way, and that difference is the authorization model.

Scope Where it lives Who sets it
organization org_id on every tenant-owned row the credential, never the body
workspace workspaces and memberships rows workspaces:write, memberships:write
project project_id on observations and claims resolved from a reference, checked against the org on write
subject subject_id, required on every observation and claim the caller
agent agent_id, optional the caller
run run_id, optional the caller

actor_id is stamped from the authenticated principal. agent_id and run_id are caller-supplied labels for grouping and filtering; they carry no authority, so writing agent_id: "agent_admin" grants nothing. project_id sits in between: the caller supplies it, and the write path rejects an id that belongs to another organization with a non-disclosing 404.

WORKSPACE IS A RECORD, NOT YET A FILTER

Observations and claims carry no workspace_id column. Workspaces and memberships are stored and auditable, but the retrieval predicate does not join them yet, so team and organization visibility behave identically for a credential inside one organization. The compiled pack names the policy it ran under — p0-org-subject-visibility-temporal — so you can tell which generation of the filter produced it.

Visibility, and what it actually enforces

Three levels, defaulting to team when an observation omits the field.

Level Eligible for retrieval by
private the principal that wrote the record, and nobody else
team any credential in the organization (workspace narrowing is planned)
organization any credential in the organization

Eligibility is part of the candidate query, not a filter over results. A record you may not read never reaches ranking, so it contributes no score, no conflict entry and no count. Compiling the same subject from two credentials shows it — the writer sees its private claim, the other principal sees candidates: 0:

{ "data": { "context_id": "ctx_46e8806af44c455fa07d2449d926c7dd",
    "scope": { "subject_id": "user_shared", "project_id": null },
    "budget": { "max_tokens": 600, "used_tokens": 0 }, "items": [], "conflicts": [] },
  "meta": { "degraded": { "semantic": false, "vector": "disabled", "model": "disabled" },
    "candidates": 0 } }

The same boundary holds on the write path. Citing another principal’s private observation as evidence returns 404, not 403 — an id you may not read is indistinguishable from an id that does not exist.

Two ceilings a credential cannot climb

Every key carries a max_trust. Asserting above it fails before any mutation:

{ "error": { "code": "FORBIDDEN",
    "message": "This credential may not assert \"verified\" trust." } }

Trust ranks unverifiedassertedverifiedpolicy_approved. A claim gets a second ceiling from its evidence: its trust may not exceed the highest-trust supporting observation, and its visibility may not exceed the narrowest source it was derived from. Both default to the inherited value, so a claim built from one private observation comes back "visibility": "private" without the caller asking for it. Consolidation cannot launder a private note into team memory.

A missing scope is a different answer from a missing ceiling:

{ "error": { "code": "FORBIDDEN", "message": "Missing required scope \"workspaces:read\"." } }

Workspaces and memberships

memberships.shbash
curl -X POST http://127.0.0.1:8787/v1/workspaces \
  -H "authorization: Bearer $TITEN_API_KEY" -H 'content-type: application/json' \
  -d '{"name":"Platform"}'
# → {"data":{"workspace_id":"ws_e8501b1e3aac4fb1ab6a12fd09fcc57e","name":"Platform"}}

curl -X POST http://127.0.0.1:8787/v1/memberships \
  -H "authorization: Bearer $TITEN_API_KEY" -H 'content-type: application/json' \
  -d '{"principal_id":"agent_reviewer","principal_kind":"agent","role":"member"}'
# → {"data":{"membership_id":"mbr_16e7a364608e4fc88b65055b0c68ab18"}}

principal_kind is human, agent or service; role is owner, admin, member or reader; workspace_id is optional and omitting it makes the membership organization-wide. A duplicate workspace name in one organization returns 409.

DELETE /v1/memberships/:id is a soft removal: the row gets a removed_at and stops appearing in GET /v1/memberships, but it is not deleted, and no record that principal wrote is touched.

Resolving a project

POST /v1/projects/resolve turns a repository reference into an opaque id, so two agents that describe the same repo differently land on one project.

curl -X POST http://127.0.0.1:8787/v1/projects/resolve \
  -H "authorization: Bearer $TITEN_API_KEY" -H 'content-type: application/json' \
  -d '{"reference":"https://github.com/RamaAditya49/Titen.git","create":true}'
{ "data": { "project_id": "project_7c5cf33d4d69422f970169b855e4069a",
    "reference": "ramaaditya49/titen", "created": true } }

Hosted Git origins normalize to lowercase owner/repo, so the .git suffix, the scheme and the capitalization all collapse. A reference that would not be shareable is rejected outright: credential material, a query string, a fragment, or a local filesystem path. create: true additionally requires projects:create — resolving is a lookup, creating is a capability — and without it an unknown reference returns 404. Neither one creates membership.

Next

↑↓ navigate↵ open