# Internal Entity Identifier Format

> Status: canonical-spec
> Canonical path: `specs/internal/identifiers.md`
> Scope: long-lived entity ID format contract across AgentTeam and MeshX
> Non-goals: protocol-only identifiers covered separately (e.g. `NewMemberID`, `NewMessageID`)

## Goal

Make every long-lived entity ID across AgentTeam and MeshX follow one uniform format
contract, so that:

- new table schemas can size ID columns once (64 chars) without per-feature analysis
- routing, audit, and debugging surfaces can recognize entity IDs by prefix
- ID collision probability is bounded uniformly across resources
- AgentTeam and MeshX share one implementation owner, preventing format drift

## Format Contract

Every long-lived entity ID MUST match:

```
{prefix}_{14 chars base36}
```

- `{prefix}` is a short, lowercase ASCII identifier for the resource type.
  Prefixes MUST NOT contain `_`.
- The `_` separator is mandatory.
- The 14-char suffix is sampled uniformly (rejection sampling on
  `crypto/rand` bytes) from `abcdefghijklmnopqrstuvwxyz0123456789`
  (36 symbols, ~5.17 bits/char).

### Entropy and collision bound

A 14-char base36 suffix provides ~72 bits of entropy. Birthday-bound 50%
collision probability is reached at roughly **9.2 × 10¹⁰** entries within a
single prefix — far beyond any realistic table size for a single tenant
or workspace.

For comparison (computed via `n ≈ √(2 · N · ln 2)` where `N = 36^length`):

| Suffix length | Entropy | 50% collision bound (per prefix) |
|---------------|---------|----------------------------------|
| 10 chars      | ~52 bit | ~7.1 × 10⁷                       |
| 12 chars      | ~62 bit | ~2.6 × 10⁹                       |
| **14 chars**  | ~72 bit | **~9.2 × 10¹⁰**                  |
| 16 chars      | ~83 bit | ~3.3 × 10¹²                      |

The 14-char choice is the project default. It is short enough to keep IDs
readable in logs and URLs, and long enough that collision resistance is never
the bottleneck of any feature that fits in a single workspace.

## Schema Design Reference

When designing a new table with an entity ID column:

- Use `varchar(64)` (Go: `gorm:"size:64"`) for ID columns. This leaves
  comfortable headroom for `{prefix}_{14 chars}` plus any future prefix
  length changes.
- Do NOT size ID columns to match the current ID length exactly (e.g. avoid
  `size:20` for a `mem_` ID). Always size for the format contract, not the
  current suffix length.

## Prefix Conventions

Prefixes are short, lowercase ASCII identifiers for a resource type. The
canonical list of prefixes in use lives in the codebase, not in this spec —
grepping for `identifiers.NewEntityID("...")` is authoritative. This section
documents the conventions and gives representative examples; it is NOT a
complete registry, and absence from this section does NOT mean a prefix is
free to claim.

### Choosing a prefix

- Lowercase ASCII, no `_` (the `_` is the separator).
- Short — typically 2–6 chars. Long resource names get abbreviated
  (`svcinstreq` for "service instance request" is the upper end).
- Stable — once a prefix is in the database, it never changes for that
  resource type.
- Distinct — before adding a new prefix, grep the codebase to confirm no
  existing caller already uses it.

### Examples (non-exhaustive)

AgentTeam-side (`github.com/tidbcloud/AgentTeam`, source: `internal/core/identifiers`):

- `ws` — workspace
- `chn` — channel
- `dm` — direct message conversation
- `agt` — agent
- `usr` — user
- `sess` — user session
- `orgm` — organization member
- `orgt` / `orgu` / `oatm` / `oaum` — org team / unit / memberships
- `app` / `appver` / `appinst` / `appsub` / `appwd` / `appconn` / `appgr` — App family
- `svc*` — service actor family (`svctb`, `svcwh`, `svced`, `svccap`, `svcgr`, ...)
- `lps` — LLM proxy session
- `rwd` — rewind operation
- `rpt` — content report
- `kqf` — Knowledge Q&A answer feedback
- `kqh` — authenticated Knowledge Q&A answer share
- `rxn` / `rxact` — reaction / reaction delivery
- `att` / `avt` / `stk` — attachment / avatar / sticker
- `mvh` — mobile verification history
- `tev` — task timeline event

AgentTeam-side protocol-only identifiers (`NewMemberID`, `NewMessageID`) follow
different contracts and are out of scope for this spec.

Resource-domain consumers (canonical owner: `internal/core/identifiers`):

- `mem` — memory resource
- `sk` — skill resource
- `rr` / `sab` / `srb` — skill resource repository / agent binding / repository branch
- `kb` / `kbg` / `ksv` — knowledge base / grant / saved view
- `src` / `run` / `rev` / `job` / `aud` — KB source / analysis run / revision / job / audit event

Resource packages import the same identifier owner directly. A domain-local
copy or forwarding facade is forbidden because it would create a second
implementation of the same format contract.

## Implementation Contract

The canonical implementation is
`internal/core/identifiers.NewEntityID(prefix string) (string, error)`.
All AgentTeam and MeshX callers use this owner directly. The implementation MUST:

1. Return `("prefix is required", err)` when `prefix == ""`.
2. Use `crypto/rand` for the random suffix — never `math/rand`.
3. Use the `abcdefghijklmnopqrstuvwxyz0123456789` alphabet.
4. Produce exactly 14 suffix chars.
5. Format the final ID as `prefix + "_" + suffix`.

Fallback behavior on `crypto/rand` failure is the caller's responsibility.
Service-layer wrappers (e.g. `newMemoryID`, `newSkillID`) MAY fall back to a
timestamp-based ID to preserve their existing signatures; this fallback path
is extremely rare and acceptable.

## Non-goals and explicit non-changes

- This spec does NOT mandate rewriting existing data. Historical rows with
  longer IDs (e.g. legacy `mem_` + 16 hex chars, `skill_` + 32 hex chars)
  remain valid; column sizes already accommodate them.
- This spec does NOT change protocol-facing identifiers. `NewMemberID`
  (12 chars) and `NewMessageID` (32 hex chars) are unchanged.
- This spec does NOT add a length-parameterized variant. All long-lived
  entity IDs use 14 chars; if a future feature needs a different length,
  that feature carries the burden of justification.
