Skip to content

URN composition

Hadron uses URNs (Uniform Resource Names) to identify every addressable entity — orgs, agents, apps, memories, nodes, users, and more. URNs are stable, globally unique, and human-readable. This page is the reference for how they're built.

The rules below are locked by spec 021 (URN composition) and apply across hadron-server, hadron-portal, hadron-client, and the MCP tool surface.

Anatomy of a URN

Every Hadron URN has the same shape:

hrn:<type>:<path>
  • hrn: — the canonical scheme prefix (Hadron Resource Name, in the spirit of AWS's arn:). It signals at a glance that the value is a Hadron reference.
  • <type> — the URN's resource type, drawn from the registered set below.
  • <path> — one or more hierarchy segments describing where the entity lives.

Scheme prefix: hrn: (canonical) and urn: (legacy)

The legacy scheme urn:<type>: is accepted on input forever — there is no deprecation window. Both hrn:agent:acme.com::coding-agent and urn:agent:acme.com::coding-agent parse identically. The server always emits the hrn: form: a urn:-prefixed input round-trips to a hrn:-prefixed canonical form (the parser records a legacy-urn-scheme rewrite for observability). Prefer hrn: in new content; you'll still see urn: in older data and it will keep working.

The path uses two delimiters:

  • :: — separates hierarchy levels (org / app / agent / memory-role / node-loc).
  • : — separates slug atoms within a single level (e.g., a type marker before a slug, or an author-org before an agent slug).

That distinction is the most important thing to internalize. Everything below builds on it.

Type registry

The complete set of URN types Hadron recognizes:

Group Types
Core agent, app, app-user, ai-config, asset, edge, license, memory, node, org, platform, reference, session, subscription, usage, user
Node types abstract, partial, parent, plan, prompt, record, task, review
Node roles chat, chat-message, config, conversation, event, goal, stage
Node parts condition, data

Types are matched case-sensitively. The list is locked but extensible — a future spec will define a registration mechanism for third-party orgs. For now, treat the set as fixed.

loc: is deprecated as a URN type. Stored data containing hrn:loc:... (or legacy urn:loc:...) segments is treated as a data defect; new code MUST use the current grammar.

Hierarchy levels by entity

The number and meaning of ::-separated levels depends on what's being addressed.

Org URN

hrn:org:<org-slug>

One level. The org slug is conventionally a domain name (micromentor.org, hadronmemory.com).

Examples:

hrn:org:hadronmemory.com
hrn:org:micromentor.org

App URN

hrn:app:<installing-org>::<app-slug>

Two levels. An App is the runtime deployment of an Agent into an org.

Examples:

hrn:app:micromentor.org::coding-app
hrn:app:hadronmemory.com::secureid

Agent URN — definition

The agent as authored by its owning org:

hrn:agent:<author-org>::<agent-slug>

Examples:

hrn:agent:hadronmemory.com::coding-agent
hrn:agent:micromentor.org::engineering-coach

Agent URN — installed (R2 form)

When an Agent is installed into another org's App, the install gets a canonical URN that embeds the author-org so two installs of same-slug agents from different authors stay distinguishable:

hrn:agent:<installing-org>::<app-slug>::<author-org>:<agent-slug>

Note the single : between <author-org> and <agent-slug> — that's not a hierarchy boundary; the author-org and agent-slug together form a single path-segment that's a verbatim sub-path of the agent's definition URN.

Examples:

hrn:agent:micromentor.org::coding-app::hadronmemory.com:coding-agent
hrn:agent:micromentor.org::coding-app::competitor.com:coding-agent

These two URNs differ only at the author-org segment — they point to two distinct installs of two same-slug agents from different authors. The author-org is the disambiguator.

If the installing org is the author org (install-by-self), the duplicate org segment is dropped:

hrn:agent:hadronmemory.com::coding-app::hadronmemory.com:coding-agent
                                                 ↓ collapses
hrn:agent:hadronmemory.com::coding-app::coding-agent

You can query this canonical install URN from the GraphQL API via the App.installedAgentUrn field — the server computes it for you.

Memory URN

A memory's URN depends on its scope. There are six shapes in total — one standalone org-owned shape and five App/Agent-scoped shapes:

# Org-owned knowledge memory (standalone, no App or Agent context)
hrn:memory:<org-slug>::<memory-slug>

# System memory of an installed agent
hrn:memory:<installing-org>::<app-slug>::<author-org>:<agent-slug>::system

# App-wide memory (no agent segment — belongs to the App globally)
hrn:memory:<installing-org>::<app-slug>::app-mem

# Per-user memory in an App
hrn:memory:<installing-org>::<app-slug>::<author-org>:<agent-slug>::app-user:<user-slug>

# Private per-user memory
hrn:memory:<installing-org>::<app-slug>::<author-org>:<agent-slug>::priv:<user-slug>

# Group-shared memory
hrn:memory:<installing-org>::<app-slug>::<author-org>:<agent-slug>::group-mem:<group-slug>

The trailing segment of the App/Agent-scoped shapes is the memory-role marker. There are exactly five:

Role Parameterized? Includes agent segment? Use
system no yes The agent's system memory (read-only at runtime).
app-mem no no App-wide memory; belongs to the App globally, not to a specific agent install.
app-user yes — slug or PK yes A user's memory inside the App.
priv yes — slug or PK yes A user's private memory (not visible to the App owner).
group-mem yes — slug or PK yes Memory shared by a named group within the App.

See Memory ownership and lifecycle for the per-role ownership and visibility rules.

Examples (canonical form throughout — author-org included where it applies):

hrn:memory:micromentor.org::shared-glossary
hrn:memory:micromentor.org::coding-app::hadronmemory.com:coding-agent::system
hrn:memory:micromentor.org::coding-app::app-mem
hrn:memory:micromentor.org::coding-app::hadronmemory.com:coding-agent::app-user:shadowbrush
hrn:memory:micromentor.org::coding-app::hadronmemory.com:coding-agent::priv:c0123abc456def789012345678

Node URN

Nodes live inside memories. The node's URN extends its memory's URN by one additional ::-segment — the node loc (the colon-separated intra-memory path):

hrn:node:<memory-path>::<node-loc>

For a node-role node, the URN can also use the role-type directly:

hrn:prompt:<memory-path>::<node-loc>
hrn:review:<memory-path>::<node-loc>
hrn:record:<memory-path>::<node-loc>

Both forms address the same node; the role-form is canonical at the surface (storage, audit logs), while hrn:node: is accepted as an alias for backward compatibility.

Example — a prompt-typed node at loc review:sort-imports in the system memory of an installed coding-agent:

hrn:prompt:micromentor.org::coding-app::hadronmemory.com:coding-agent::system::review:sort-imports

Note the node loc review:sort-imports keeps its internal : unchanged — node locs are free-form intra-memory paths and aren't ::-separated.

Slug rules

Every slug atom — the :-separated pieces within a path-segment — follows the same rules:

  • Charset: letters, digits, dots, underscores, and hyphens. Must start and end with a letter or digit.
  • Regex: /^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$/
  • Length: 64 characters maximum.
  • Case-sensitive storage; case-insensitive reserved-word check.
  • Reserved words: the full URN-type registry plus the role-marker names — see below.

The charset deliberately allows dots, so domain-name slugs like a.b.c.com and dotted-namespace identifiers work uniformly.

Reserved words

A slug equal (case-insensitive) to any of these is rejected at entity-create time:

  • All URN types: agent, app, app-user, ai-config, asset, edge, license, loc, memory, node, org, platform, reference, session, subscription, usage, user, abstract, partial, parent, plan, prompt, record, task, review, chat, chat-message, config, conversation, event, goal, stage, condition, data
  • All memory-role markers: system, app-mem, app-user, group-mem, priv

loc is in the reserved-word list even though loc: is deprecated — future-proofing the namespace.

The check is case-insensitive: Agent, AGENT, aGeNt all fail.

ID or slug at parameterized role-marker positions

For the three parameterized memory-role markers (app-user, priv, group-mem), Hadron's server dispatches on the shape of the parameter:

  • CUID (^c[a-z0-9]{24}$) or 32-char hex PK (^[0-9a-f]{32}$) → resolved as an internal ID.
  • Anything else → resolved as a slug.

The 32-char hex pattern is named UUIDV7_HEX in the server source for historical reasons but accepts any 32-character lowercase hex string; it doesn't enforce the UUIDv7 version/variant nibbles.

Both forms address the same memory; backend code can pass the PK it holds, UI code can pass the slug:

hrn:memory:micromentor.org::coding-app::hadronmemory.com:coding-agent::app-user:shadowbrush
hrn:memory:micromentor.org::coding-app::hadronmemory.com:coding-agent::app-user:c0123abc456def789012345678

The two URNs resolve to the same memory.

Aliases and canonical forms

Hadron accepts several alias forms at input but emits a single canonical form in storage, audit logs, and API responses. There are five alias categories.

0. Legacy urn: scheme

The legacy urn: scheme prefix is an alias for the canonical hrn: scheme. Accepted on input forever (no deprecation); always emitted as hrn::

Form Example
Alias urn:agent:micromentor.org::coding-app::hadronmemory.com:coding-agent
Canonical hrn:agent:micromentor.org::coding-app::hadronmemory.com:coding-agent

1. Source / installation memory

The install-by-self URN is an alias for the definition URN:

Form Example
Alias hrn:memory:hadronmemory.com::hadronmemory.com:coding-agent::system
Canonical hrn:memory:hadronmemory.com::coding-agent::system

2. Node-role polymorphism

A node with a known role is addressable as either hrn:node: or hrn:<role>::

Form Example
Alias hrn:node:micromentor.org::coding-app::hadronmemory.com:coding-agent::system::review
Canonical hrn:review:micromentor.org::coding-app::hadronmemory.com:coding-agent::system::review

3. Path/slug shortening

When the slug is unique in scope, the shortened form omits the author-org:

Form Example
Alias hrn:agent:micromentor.org::coding-app::coding-agent
Canonical hrn:agent:micromentor.org::coding-app::hadronmemory.com:coding-agent

The canonical form always includes the author-org so an audit entry doesn't silently shift meaning if a future install adds a same-slug agent from a different author.

4. Type-marker optionality

Type markers (app:, agent:, memory:) inside path-segments are optional:

Form Example
Alias hrn:agent:micromentor.org::app:coding-app::agent:hadronmemory.com:coding-agent
Canonical hrn:agent:micromentor.org::coding-app::hadronmemory.com:coding-agent

The canonical form is the bare shape, without the markers.

Case sensitivity

URN slug storage is case-sensitive. hrn:agent:org::Coding-Agent and hrn:agent:org::coding-agent are two different URNs — they identify two different agents.

The reserved-word check is the only case-insensitive part of slug validation; it prevents users from creating slugs like Agent or aGENT that would collide with the reserved type names.

Deprecated: the loc: prefix

The pre-2026-05 loc: sugar — a relative-path shorthand for node locs — is removed entirely. The parser rejects any URN containing a loc: segment.

Stored data with loc: segments is treated as a data defect; no transparent migration is provided.

Where to use the canonical form

Surface Form
GraphQL responses Canonical
MCP tool inputs Any alias accepted; canonical persisted
Audit logs Canonical
Portal <Urn> rendering Canonical
Database storage Canonical
User-pasted inputs Any alias accepted

Two callers submitting two different alias forms for the same logical URN produce byte-identical persisted records and audit entries.

API input contract — URN inputs must be fully qualified

Per spec 022, every API surface that accepts a URN — MCP tool, GraphQL resolver, hadron-client SDK function — requires URN inputs to be fully qualified. (ID-shape inputs and shorthand bare forms are accepted per the exceptions below.)

Entity type Minimum shape Examples
org <org> acme.com or hrn:org:acme.com
memory <org>:<memory> acme.com:mmdata or hrn:memory:acme.com::mmdata
agent / app <org>:<slug> acme.com:coding-agent or hrn:agent:acme.com::coding-agent
node <org>:<memory>:<loc> acme.com:mmdata:review:sort-imports or hrn:node:acme.com::mmdata::review:sort-imports

The prefixed examples use the canonical hrn: scheme; the legacy urn: scheme (e.g. urn:node:acme.com::mmdata::review:sort-imports) is equally accepted on input.

Relative-form URNs (review:sort-imports without the <org>:<memory> prefix) are rejected at the API boundary with:

URN "review:sort-imports" is not fully qualified.
Expected a node URN with at least 3 hierarchy segments
(org:memory:loc, e.g., "acme.com:mmdata:review:sort-imports").

GraphQL surfaces this as a typed error: { extensions: { code: "URN_NOT_QUALIFIED" } }. MCP tool error results carry the same code. The error message embeds the offending value AND the canonical-form fix template.

Why fully-qualified at the API boundary?

A user prompt like "add a review node 'sort-imports'" lets the agent construct one of two things:

  • Relative: review:sort-imports — depends on a hidden "active memory" the user can't see.
  • Fully qualified: acme.com:mmdata:review:sort-imports — picks one memory unambiguously.

With one writable memory in an org, the two were equivalent. With two writable memories (e.g., mmdata and mm-app, both containing a review section), the relative form silently routes to whichever memory the server's fallback heuristics pick — and the user has no visibility into the choice.

Spec 022 closes that path: the agent layer is responsible for picking the memory (it asks the user when ambiguous), and the API rejects anything less than a fully-qualified URN. The fallback heuristics are removed.

Exception: ID-shape inputs

Spec 007's ID-or-URN dispatch is preserved: inputs matching the ID regexes (^c[a-z0-9]{24}$ CUID or ^[0-9a-f]{32}$ 32-char hex) are treated as internal IDs and bypass the qualification rule. Callers that already hold an entity's ID continue to work without changes.

Exception: content text

The qualification rule applies at the API boundary, not inside content prose. A YAML import or a node's markdown body may legitimately contain relative URNs, loc:-style historical references, or other non-qualified strings — those are parsed by import / render code that intentionally accepts looser forms. Only inputs flowing through GraphQL inputs, MCP tool arguments, or SDK call signatures are subject to the qualification rule.

See spec 007 (Entity Reference Convention) FR-019 for the canonical statement of the rule, and spec 022 (Fully-Qualified URN Inputs) for the design discussion and per-tool enforcement.