@hasna/identities
v0.5.14
Published
Open identity records for humans and AI agents
Maintainers
Readme
@hasna/identities
Open identity records for humans and AI agents.
open-identities treats an identity as more than an ID. An identity can carry a human or agent name, a durable unique identifier, verified email addresses, phone numbers, integration sync references, and narrative documents such as PROMPT.md, SOUL.md, PERSONALITY.md, and ETHOS.md.
Install
npm install -g @hasna/identitiesCLI
identities --help
identities create --kind agent --name "Ava Example" --identifier agent:ava-example --email [email protected] --phone +15555550123
identities list
identities show agent:ava-example
identities show agent:ava-example --verbose
identities update agent:ava-example --identifier agent:ava-renamed
identities link-email agent:ava-example [email protected] --verified --mailery-id mailery-address-id
identities link-phone agent:ava-example +15555550199
identities machine assign agent:ava-example machine-a --purpose browserplan --slot profile-01 --json
identities browserplan reserve agent:ava-example --machine machine-a --slot profile-01 --json
identities browserplan coverage --target 8 --json
identities doc set agent:ava-example ethos "Protect user intent and identity data."
identities doc get agent:ava-example ethos --json
identities doc export agent:ava-example --dir ./ava-documents --json
identities instructions set global --kind global-rules --title "Global Safety" --content "Never expose secrets." --rule-id safety:no-secrets --provider codewith --non-overridable --json
identities instructions list --json
identities instructions validate --json
identities instructions export ./instructions.json --json
identities instructions sources --canonical --provider codewith --json
identities agent manifest agent:ava-example --json
identities agent seed-roster --roster ./team.roster.json --docs-dir ./agents --json
identities eve export agent:ava-example --out ./ava-agent
identities status --json
identities media doctor --json
identities media generate-voice agent:ava-example --dry-run --json
identities media generate-profile-image agent:ava-example --dry-run --json
identities media generate-roster --voices --profile-images --dry-run --json
identities validate --json
identities status --jsonupdate <target> --identifier scheme:value renames the durable unique identifier. The previous identifier is kept as a secondary identifier so existing references keep resolving, renames onto an identifier held by another identity fail without changes, and a rename-identifier audit event is recorded.
Local data is stored in SQLite at ~/.hasna/identities/identities.json by default.
Use OPEN_IDENTITIES_STORE=/path/to/identities.sqlite or --store <path> for isolated scripts and tests. When --store <path> is used, the CLI writes audit events to <path>.audit.jsonl unless --audit <path> is provided.
Generated media assets are stored in ~/.hasna/identities/assets by default. Use OPEN_IDENTITIES_ASSETS_DIR or --out-dir <dir> to place generated audio and profile images somewhere else.
CLI human output is compact by default so agent terminals do not ingest full identity records, document bodies, manifests, media objects, or coverage JSON unless explicitly requested. Use:
--jsonfor stable machine-readable contracts and full exported objects--verbosefor full human-side object details--limit <n>for longer human tables such aslist,machine list,browserplan list,media status, andasset list- detail commands such as
show <id> --verbose,doc get <id> <key> --verbose, andagent manifest <id> --jsonwhen a full record is actually needed
Plain doc get writes the complete document body byte-for-byte for shell
consumers. Structured --json and --verbose reads include { key, value }.
Full agent manifests include the same canonical document bodies; compact human
manifest output continues to omit them.
doc export writes each populated canonical Markdown document slot plus
.identities-documents.json. The manifest uses the versioned
hasna.identities.documents/v1 contract and records the package version,
export time, source identity update time, populated count, byte length, and
SHA-256 of every body. Consumers can reject unsupported contract versions,
compare source.identityUpdatedAt with the current identity, and verify hashes
instead of treating an unversioned directory as current. Empty and
whitespace-only slots have manifest entries but no Markdown file; a later
export removes any stale canonical file for a slot that became empty. The old
manifest is removed before files are refreshed and the new one is written last,
so a failed partial refresh cannot leave stale metadata certifying mixed files.
identities status --json is the public-safe machine contract for fleet
integrations. It reports store path, audit path, environment override state,
identity/contact/document/media counts, document key names, and safety flags. It
does not include contact values, document bodies, media asset paths, credential
values, or sensitive identifiers.
identities status --json emits a metadata-only reference contract for fleet
consumers. It reports package version, redacted store paths, env override
names, roster and role counts, aggregate contact/document counts, and opaque
identity refs. It does not include names, email addresses, phone numbers,
identifier values, document bodies, credentials, private keys, GitHub App
private data, or raw env values.
Hasna Company Agent Roster
A roster is a JSON file the operator supplies. It names the agents, the organization they belong to, the domain their mailboxes live on, and any identifiers a previous roster version created that should now be pruned.
Rosters are data, not source. This repository ships exactly one — the synthetic three-agent example in rosters/example.roster.json, on the RFC 2606 reserved domain example.com — so the feature is demonstrable and testable from a clean checkout. Keep a real roster outside this repository and point the CLI at it.
identities agent seed-roster --roster ./team.roster.json --docs-dir ./agents --jsonResolution order for the roster file is --roster <file>, then IDENTITIES_ROSTER_FILE, then the bundled example. The command
prints which one it used, and says so plainly when it fell back to the example. A deployment that carries no rosters/ directory
is fine: an explicit --roster never touches the bundled example.
identities status takes the same --roster <file> flag and resolves it the same way, so its roster counters describe the roster
you actually seeded. When no roster resolves it reports unresolvedReason rather than a zero, and identities seeded from some
other roster are counted under seededOtherRoster rather than disappearing.
The seed command upserts the roster into the selected identity store, prunes the roster's deprecatedIdentifiers by default, and
optionally exports per-agent markdown. Each agent gets exactly one mailbox, <slug>@<emailDomain> — so the example roster produces
[email protected], [email protected], and [email protected]. Roster identities do not receive secondary mailboxes.
--docs-dir output is generated data about real agents. Write it somewhere the operator controls and leave it out of version
control: agents/ is in .gitignore, and the packed-artifact guard fails the build if it ever reappears in a release.
{
"name": "team",
"organization": "Example Org",
"version": 1,
"emailDomain": "example.com",
"deprecatedIdentifiers": ["agent:retired-role"],
"agents": [
{
"slug": "ada",
"fullName": "Ada Example Platform Engineer",
"role": "platform engineering and release readiness",
"department": "Engineering",
"vertical": "platform",
"capabilities": ["Maintain build, test, and release pipelines"],
"tools": ["example-tasks"]
}
]
}Every seeded agent also receives planned media metadata:
voice: ElevenLabs voice design/TTS settings and sample textprofileImage: MiniMax image generation prompt and model settingsassets: generated media history, with file paths, checksums, provider, model, prompt, and status
Generate media for one identity:
identities media generate-voice agent:ada --json
identities media generate-profile-image agent:ada --json
identities asset list agent:ada --jsonGenerate media for a whole roster:
identities media generate-roster --roster ./team.roster.json --voices --profile-images --jsonmedia doctor reports whether supported provider keys are available without printing secret values. ElevenLabs checks ELEVENLABS_API_KEY, XI_API_KEY, and HASNAXYZ_ELEVENLABS_LIVE_API_KEY. MiniMax checks MINIMAX_API_KEY, HASNAXYZ_MINIMAX_LIVE_API_KEY, HASNA_TAKUMI_LIVE_MINIMAX_API_KEY, and compatible secrets vault entries.
SDK
import { IdentityStore, getIdentityReferenceStatus, syncIdentityContactPointsAndUpdate } from "@hasna/identities";
const store = new IdentityStore();
const identity = await store.create({
kind: "agent",
fullName: "Ava Example",
uniqueIdentifier: "agent:ava-example",
emails: ["[email protected]"],
phones: ["+15555550123"],
documents: {
prompt: "You are Ava Example.",
personality: "Direct, careful, and helpful.",
ethos: "Protect user intent and identity data.",
},
});
await syncIdentityContactPointsAndUpdate(store, identity.id, {
mailery: {
async upsertIdentityEmail(input) {
// Implement with @hasna/mailery. input.uniqueIdentifier is always non-sensitive.
return { externalId: input.email.maileryId };
},
},
telephony: {
async upsertIdentityPhone(input) {
// Implement with @hasna/telephony.
return { externalId: input.phone.telephonyId };
},
},
});
const status = await getIdentityReferenceStatus(store);
console.log(status.counts.roster.configuredAgents);Generate and attach media through the SDK:
import { IdentityStore, generateIdentityProfileImage, generateIdentityVoice } from "@hasna/identities";
const store = new IdentityStore();
await generateIdentityVoice(store, "agent:ava-example");
await generateIdentityProfileImage(store, "agent:ava-example");Identity Shape
An identity contains:
kind:human,agent,organization, orservicefullNameand optionaldisplayNameuniqueIdentifier: a durable identifier such asopen-identities:oid_*,agent:<slug>, or another issuer-specific schemeidentifiers: additional identifiers, including sensitive identifiers when explicitly neededemails: email addresses that can be synchronized with Maileryphones: phone numbers that can be synchronized with Telephonydocuments:bio,prompt,soul,personality,ethos,capabilities,boundaries,tools,relationships,goals,context,memory,consent, andvoicevoice: generated or planned voice profile metadataprofileImage: generated or planned profile image metadataassets: generated or imported media refs, including paths, checksums, provider, model, prompt, status, and visibility metadataagent: role, model, capabilities, tools, skills, channels, schedules, and subagentstraitsandmetadata: extension fields for application-specific data
Sensitive government identifiers should be marked with sensitive: true and should only be stored when there is a legitimate operational need.
Default contact cards, agent manifests, Eve exports, and sync payloads use a non-sensitive public identifier when the canonical unique identifier is marked sensitive.
Integration Direction
The repo currently defines adapter contracts instead of hard-coding package dependencies. The adjacent email package is @hasna/mailery, with a separate @hasna/emails-sdk. The phone package is @hasna/telephony.
See docs/integrations.md for the first sync contract.
See docs/browserplan.md for the BrowserPlan machine, identity, email, and profile reservation contract.
See docs/media.md for voice and profile image generation.
See docs/instructions.md for the instruction-source schema, precedence, export contract, and fail-closed safety rules.
See docs/identity-contract.md for the versioned canonical agent identity, scoped handle/alias resolver, runtime-context, five-part source-lineage revision history, and dry-run migration contracts. Cross-repo consumers can vendor the V1 conformance fixture, identified as hasna.identities.agent-identity/v1/conformance/1; the SDK exports its repository-relative path and SHA-256 fingerprint for deterministic pinning without machine paths or network tests.
App-bound adversarial review checks
AdversarialReviewCheckPublisher publishes the adversarial-review GitHub
Check from a configured GitHub App. A reviewer-start event creates an
in_progress check. Only a structured terminal GO or NO_GO event bound to
the same repository, pull request, base SHA, head SHA, acceptance scope,
reviewer identity, and implementer identity can complete it. Public comments
and GitHub review text are deliberately absent from the publisher interface and
are never verdict inputs.
Reviewer and implementer actors must resolve through active, authoritative
source mappings in a CanonicalIdentityDirectoryV1. A head change or a base
retarget invalidates the old check and creates a new pending generation, even
when the head SHA is unchanged. Exact duplicate event IDs are idempotent;
changed-content replays fail closed. Use
FileAdversarialReviewCheckStateStore for owner-only, restart-safe state and
FileGitHubAppInstallationTokenProvider with an explicit owner-only private-key
path plus a required installation-token scope. The provider accepts exactly the
conversations repository and only the review gate's checks: write and
pull_requests: read permissions; any other scope fails closed before key
access. Key material and minted installation tokens are never included in check
output, state, or error messages.
Scoped access-token contract
@hasna/identities exports a reusable asymmetric JWT contract for local and
server-side consumers. It validates configured issuer, audience, and
public-key algorithms; requires sub, tenant, session, scopes, iat,
nbf, exp, and jti; enforces tenant and scope requirements; and checks
only SHA-256 forms of token IDs and session-family IDs against the caller's
revocation store. Unknown, revoked, unpublished, or out-of-window signing keys
fail closed.
IdentityJwksRegistry publishes standard public JWK fields plus rotation status
for active and retiring keys. Revoked keys and private JWK members are never
published; revoked_kids carries value-free revocation tombstones, and
consumers can fence rollback with minimumJwksRevision.
createIdentityAuthApi provides composable
GET /.well-known/jwks.json and POST /v1/auth/verify handlers; it does not
load signing keys or silently enable a deployment.
Operators can verify a token without putting it in shell arguments:
identities auth verify \
--token-file /owner-only/access-token \
--jwks-file /etc/identities/public-jwks.json \
--token-state-file /var/lib/identities/hashed-token-state.json \
--issuer https://identity.example \
--audience infinity-local \
--algorithm EdDSA \
--tenant tenant-acme \
--scope runs:writeThe token file must be owner-only. The state file contains only lowercase
SHA-256 hashes and session-family statuses. Private signing keys remain
caller-owned and are accepted only as runtime inputs to
issueIdentityAccessToken.
Instruction Sources
OpenIdentities owns the canonical instruction-source graph for humans, agents, personas, accounts, machines, projects, sessions, global rules, and provider rules. OpenConfigs and launchers should consume this graph and render tool-native files; they should not duplicate identity/persona documents.
Instruction sources carry:
kind:global-rules,provider-rules,global-system-prompt,provider-system-prompt,identity-doc,persona-doc,account-overlay,machine-overlay,project-overlay, orsession-overlayowner: global, provider, identity/persona, account, machine, project, or session owner refsprecedence,mergePolicy(appendorreplace),replacementScope,ruleIds,targetProviders, provider compatibility, globs, source paths, editable path markers, sensitivity, provenance, and SHA-256 hashes- fail-closed safety flags: non-overridable safety sources must append, must
declare rule IDs, cannot be replaced by later sources, cannot conflict with a
duplicate rule ID, and cannot carry
secretsensitivity
CLI examples:
identities instructions set global \
--kind global-rules \
--title "Global Safety Rules" \
--content "Never expose API keys, tokens, or secrets." \
--rule-id safety:no-secrets \
--provider codewith \
--editable-source-path /home/hasna/CODEWITH.md \
--non-overridable \
--json
identities instructions set \
--kind provider-system-prompt \
--owner-kind provider \
--owner-id codewith \
--title "Codewith System Prompt" \
--content "Render through the Codewith provider adapter." \
--provider codewith \
--compat codewith:managed-block:true \
--json
identities instructions list --json
identities instructions paths --json
identities instructions show <source-id> --json
identities instructions validate --json
identities instructions export ./instructions.json --json
identities instructions import ./instructions.json --json
identities instructions sources --json
identities instructions sources --canonical --provider codewith --json
identities instructions export --canonical --provider antigravity --json
identities instructions export --canonical --provider codewith --json
identities instructions export --canonical --provider opencode --jsoninstructions list includes store-level global/provider sources, explicit
identity sources, and derived sources from populated identity documents such as
prompt, personality, ethos, and voice. The production export contract is
{ version: 1, package: "@hasna/identities", exportedAt, sources, validation,
metadata }; downstream renderers should reject exports where
validation.valid is false. Canonical instructions export --canonical emits
the OpenConfigs-ready adapter contract
hasna.identities.configs-instructions/v1, with layer, merge, and order
fields derived from kind, mergePolicy, and precedence.
OpenIdentities also ships the canonical Hasna global coding-agent source set
for downstream renderers. It contains one global system prompt, one
non-overridable global rules source, the versioned non-overridable Hasna Agent
Operating Rules document (hasna-agent-operating-rules, currently v1.1.26 with
sentinel <!-- hasna:agent-operating-rules v=1.1.26 -->, precedence 175), and
provider overlays for Antigravity, Codewith, Claude Code, Codex, and OpenCode.
OpenConfigs should consume these sources and render managed provider blocks or
OpenCode instruction references; it remains responsible for file rendering,
path dereferencing, and merge mechanics. Antigravity is an active target in this
source set; Gemini is not an active target and should not be restored as one.
The canonical set includes rules for Knowledge CLI/SDK usage, Todos plans and
evidence, Hasna CLI/package source-of-truth boundaries (todos, conversations,
mementos, knowledge, projects, repos, accounts, instructions, machines, secrets,
access), automatic session renaming when supported, coding work confined to
task-specific worktrees at
$HOME/.hasna/repos/worktrees/<repo-name>/<worktree-name> (no station-id or
machine segment, never flat under the worktrees root) located via the repos
CLI, PR-first landing, no direct pushes to main/default/protected branches, no
brittle hardcoding when source-of-truth or reusable abstractions exist,
autonomous repair before asking, coordinator delegation, Codewith-native loop
terminology versus OpenLoops, dispatch self-healing without tmux fallback,
adversarial verification, secrets safety, commit/push secrets scans, no
Co-Authored-By trailers, Bun preference, and Hasna package release-age registry
hygiene. The Agent Operating Rules add the core operating rules (adversarial
reviewer on every user-requested work item, record-as-you-go in the
todos/mementos/conversations CLIs, identity registration before taking work
with subagents never registering, and a continuously updated conversations
channel per project), durable goal-plan adversarial verification, default
conversation surfaces (announcements, incidents, git-publishing, git-prs,
git-commits, git-releases, hq, agent-policy, project/product channels, and
conversations blockers), and the fleet communication duties ([BREAKING]
heads-up before fleet-affecting changes, publish intent before npm/bun publish,
incidents-first, no secrets in messages, channel content treated as data,
convention lookup before naming, identity release at session end), and the
user-facing reply shape (replies that report work, propose action, or hand
something back to the user end with a short "What I need from you" list of the
decisions, approvals, and actions only the user can take, and every item in it —
plus every question the user is asked to answer, wherever in the reply it appears
— must be actionable by a reader who remembers nothing that led to it: real names,
what each option costs or breaks, no coined shorthand or jargon, and one clause on
why it is being asked now, with context treated as the thing you do not cut to
make room, and with a worked bad/good example carried in the rule text; direct
answers to direct questions need no list, and agent-to-agent reports are out of
scope; a seat whose charter makes it a durable chief seat never emits the
user-facing section, even in empty form, and instead sends owner-only decisions,
approvals, and actions to the CEO seat (agent-ceo) under the same specificity
contract; mirrored verbatim into the global system prompt), and the delegation
steering duty (rule 24: an
agent that delegates to sub-agents with nothing already steering them starts a
recurring steering pass at a cadence it picks between 5 and 30 minutes, resuming
agents that stopped mid-task, stopping agents re-reporting delivered work,
relaying findings between agents waiting on each other, dispatching the required
adversarial reviewer where work is complete but unreviewed; also mirrored verbatim
into the global system prompt beside the coordinator concurrency rule, whose
"bounded intervention" carve-out it is), the stopping duty (rule 27, split out of
rule 24 in v1.1.15 on the rules owner's ruling, with every clause carried over
verbatim: on each scheduled steering pass the agent decides explicitly whether that
pass is the last one and whether any review cycle under it should end, and records
why — a review cycle's end condition is settled before the cycle starts and has to
be reachable while findings still exist, so a cycle that ends only when the
reviewer returns nothing is ruled out as the never-terminating test worded as a
stop, a reviewer still finding things is ruled out as a reason to continue,
stopping is a decision made rather than drifted into, and when the delegated work
is done the pass ends), the dispatched-agent liveness duty (rule 25: dispatched agents are not left
to go stale — every agent still outstanding is checked on each scheduled steering
pass or on the cycle of whatever else is already steering them, recent output or
a heartbeat counts as healthy, roughly ten minutes of silence earns a direct
one-line status request, and replacement waits for roughly thirty minutes of
silence or two unanswered probes and never happens to an agent whose probe has
not been waited out; the probing is named as that pass or cycle's bounded
intervention so it does not contradict the non-overridable rule against
repeatedly polling workers; throttling is distinguished from death, because a
high or rising API 529/overload retry count means the agent is alive and being
rate-limited and killing it discards its whole accumulated context for nothing;
an agent that did die to an overload error is re-dispatched, a live one is never
duplicated, liveness is read from a transcript's modification time and a bounded
tail rather than a full transcript and judged from at least two independent
signals, and because agents do die mid-flight the rule points back at rule 2's
record-as-you-go duty instead of restating it) and the background-agent heartbeat
duty (rule 26: an agent working in the background makes its own liveness
observable, because a coordinator reading a transcript cannot — refreshing its
last-seen marker at roughly ten-minute intervals and putting one line of current
status on the surfaces rule 2 already requires; a heartbeat sets a cadence and
adds a marker, is never a second account of the work, and is owed whether or not
anyone has asked for it). All defer to rule 2 rather than restating it, and all
are mirrored verbatim into the global system prompt, where rule 27 renders
immediately after rule 24 — the system prompt has no numbering, so nothing there
forces the operating-rules document's append-don't-renumber ordering and the pair
reads as the one duty it is. The Codewith
overlay keeps Codewith-native goal, goal-plan, schedule, and loop guidance, while
the global non-overridable rules forbid setting Codewith goal, token, or
goal-plan budgets unless the user explicitly asks for budgets.
SDK consumers can import the same data from @hasna/identities:
import {
createGlobalAgentConfigsInstructionSourceExport,
createGlobalAgentInstructionSourceExport,
} from "@hasna/identities";
const rawExportForCodewith = createGlobalAgentInstructionSourceExport({
providers: ["codewith"],
});
const openConfigsExportForOpenCode = createGlobalAgentConfigsInstructionSourceExport({
providers: ["opencode"],
});Vercel Eve
open-identities can generate Eve-compatible agent directories:
identities eve export agent:ava-example --out ./ava-agentThe exporter writes agent/instructions.md, agent/identity.json, agent/agent.ts, agent/tools/resolve_identity.ts, and skill documents derived from identity docs. The generated Eve files are derived artifacts; open-identities remains the canonical source of the identity.
Development
bun test
bun run build
bun run verify:releaseLicense
Apache-2.0 -- see LICENSE
