> For the complete documentation index, see [llms.txt](https://5code.gitbook.io/5code-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://5code.gitbook.io/5code-docs/5code-recognition/integrations/nametags-and-chat.md).

# Nametags & Chat

A name over someone's head, or a name in chat, should appear only for the people whose **character** actually recognises the speaker. That is what this integration does.

```lua
Config.NametagSystem = 'none'   -- off by default
```

{% hint style="info" %}
**It is off by default because it changes how your whole server reads.** Once names depend on recognition, every masked robbery, every disguise and every first meeting looks different at a glance. Turn it on deliberately, and tell your players.
{% endhint %}

## Support

| System                | Support   | Notes                                                         |
| --------------------- | --------- | ------------------------------------------------------------- |
| our exports and event | Supported | Any nametag or chat script can use them.                      |
| cxc\_gamertag         | Stub      | Built-in mask driver, plus a snippet for the per-viewer name. |
| zeno-rpchatid         | Snippet   | Edit their own `Config.IsPlayerMasked`.                       |
| any other             | Snippet   | The push-cache pattern.                                       |

{% hint style="warning" %}
**We do not ship, bundle or modify any third-party resource.** Every snippet goes into **your** file, or into the third-party script's own open config file, by your hand. Some of those scripts are escrowed, and some are licensed so their code may not be reused — which is exactly why we publish snippets instead of patches.
{% endhint %}

## Ready-made snippets

Ready-made snippets, one page each:

| Page                                                                                                                          | For                                                     |
| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| [Any nametag script](/5code-docs/5code-recognition/integrations/nametags-and-chat/integration-snippets/any-nametag.md)        | Any overhead-nametag resource — the push-cache pattern. |
| [cxc\_gamertag](/5code-docs/5code-recognition/integrations/nametags-and-chat/integration-snippets/cxc_gamertag.md)            | The mask driver plus the per-viewer name patch.         |
| [zeno-rpchatid](/5code-docs/5code-recognition/integrations/nametags-and-chat/integration-snippets/zeno-rpchatid.md)           | `Config.IsPlayerMasked`.                                |
| [Nobody NameTags](/5code-docs/5code-recognition/integrations/nametags-and-chat/integration-snippets/nobody-nametags.md)       | `Config.CustomRequestScript`.                           |
| [Default chat](/5code-docs/5code-recognition/integrations/nametags-and-chat/integration-snippets/chat-default.md)             | The default `chat` resource: a per-recipient `/me`.     |
| [CodeM Supreme Chat](/5code-docs/5code-recognition/integrations/nametags-and-chat/integration-snippets/codem-supreme-chat.md) | `showPlayerData`.                                       |

{% hint style="info" %}
These pages live in this documentation, **not** in the resource zip: `docs/` is never packaged. Copy the snippet you need from here.
{% endhint %}

## The rule the snippets implement

> A name is shown **only while the viewer has a live scene result** of that player — they scanned them, it worked, and it was within the last ten minutes or so.

Never from a saved memory alone. Never from a Lab ID. And **never a real character name**: what we hand back is the name the target introduced themselves with, or the memory line ("Met him on Vinewood Blvd in the evening").

Two settings control it:

| Key                            | Default   | Allowed                         | What it does                                                                                   |
| ------------------------------ | --------- | ------------------------------- | ---------------------------------------------------------------------------------------------- |
| `Config.Api.RecognitionSource` | `'scene'` | `'scene'` · `'scene_or_memory'` | `scene` = only a live result counts. `scene_or_memory` = any saved memory counts too.          |
| `Config.Api.LabInNameApis`     | `false`   | `true` / `false`                | Whether Lab IDs also count for name display. They always count for proofs and `HasRecognized`. |

{% hint style="info" %}
**Why `'scene'` rather than `'scene_or_memory'`.** A memory can be five days old. Your character remembers that person, but they are not looking at them right now — and a name that pops up the moment a remembered player walks into render distance is a radar, not a memory.
{% endhint %}

## What you may call

| Side   | Export                | Signature                                                                                  |
| ------ | --------------------- | ------------------------------------------------------------------------------------------ |
| server | `IsRecognized`        | `(viewerSrc, targetSrc, minPct?, opts?)` → `boolean`                                       |
| server | `GetRecognitionLabel` | `(viewerSrc, targetSrc, minPct?, opts?)` → `string\|nil`                                   |
| server | `GetRecognition`      | `(viewerSrc, targetSrc, opts?)` → `nil` or an info table                                   |
| server | `GetRecognizers`      | `(targetSrc, minPct?, opts?)` → list of server ids                                         |
| server | `HasRecognized`       | `(observerSrc, targetSrc, minPct?, opts?)` → `boolean` ("may act"; always counts a Lab ID) |
| server | `GetReliability`      | `(observerSrc, targetSrc, opts?)` → `0`–`100`                                              |
| server | `IsFaceHidden`        | `(targetSrc)` → `boolean` (viewer-independent)                                             |
| server | `GetConcealment`      | `(targetSrc)` → a table describing what is visible                                         |
| client | `IsRecognized`        | `(targetServerId, minPct?)` → `boolean`                                                    |
| client | `GetRecognitionLabel` | `(targetServerId, minPct?)` → `string\|nil`                                                |
| client | `GetRecognition`      | `(targetServerId)` → `nil` or an info table                                                |
| client | `IsFaceHidden`        | `()` → `boolean` — your own face                                                           |

`minPct` defaults to `Config.Scoring.ActThreshold` (80).

These are all **read** exports: no permission list, no `TrustedResources` entry needed. Full signatures and return shapes: [Server Exports](/5code-docs/5code-recognition/developers/server-exports.md) and [Client Exports](/5code-docs/5code-recognition/developers/client-exports.md).

## The client event

```lua
AddEventHandler('5code-recognition:client:onRecognitionChanged', function(targetServerId, info)
    -- info = nil, or { pct, channel, live, label, introducedName, sceneEndsAt }
end)
```

Local to the client. Any resource on the same client may listen.

## Three rules you must not break

{% hint style="danger" %}
**1. Never poll an export per frame.** Every cross-resource export call is serialised. Listen to `onRecognitionChanged`, keep a small local table, and read *that* in your draw loop. The `any-nametag.md` snippet is that pattern.
{% endhint %}

{% hint style="danger" %}
**2. Always apply the threshold.** A live scene also exists for a *first* look at a stranger, at whatever that scan was worth. Drawing `info.label` without checking `info.pct` puts a label over the head of somebody the character did not recognise — and then the scan stops meaning anything.
{% endhint %}

{% hint style="danger" %}
**3. Never copy our answers into a state bag, `GlobalState` or any shared table.** That publishes who knows whom to every client on the server. Our own code writes none, ever. Keep the table local to the viewer's client, or send per-recipient messages from the server — which is why `GetRecognizers` is server-only.
{% endhint %}

## cxc\_gamertag

Built-in driver. With `Config.NametagSystem = 'cxc_gamertag'` we call their `Mask` function when a player's face becomes hidden or visible, so masked players lose their tag.

Their export name is renameable on their side; if you renamed it, adjust the branch in `bridge/nametag_client.lua`.

The per-viewer *name* (rather than just the mask) needs the patch in [the cxc\_gamertag snippet](/5code-docs/5code-recognition/integrations/nametags-and-chat/integration-snippets/cxc_gamertag.md).

## zeno-rpchatid

Snippet only, and the adapter reminds you at boot:

```
zeno-rpchatid: edit their Config.IsPlayerMasked to call IsFaceHidden. Never write their rp_masked state bag.
```

Edit **their** `Config.IsPlayerMasked` to call our `IsFaceHidden`. We never write their state bag — that is their data, and writing it from a second resource is how two scripts end up fighting over one value.

## Any other nametag script

Use the push-cache pattern from [Any nametag script](/5code-docs/5code-recognition/integrations/nametags-and-chat/integration-snippets/any-nametag.md):

1. Listen to `5code-recognition:client:onRecognitionChanged`.
2. Store `{ pct, label, introducedName }` in a local table, keyed by server id.
3. In your draw loop, read that table. No exports, no calls, no allocation.
4. Apply `minPct` before you draw anything.

## Chat

The same rule, one step further: a `/me` or an OOC line that names the speaker should be sent **per recipient**, so each player sees the name their character actually knows.

`GetRecognizers(targetSrc, minPct)` gives you the list of server ids who currently recognise that player. Send the named version to them and the anonymous version to everyone else.

[Default chat](/5code-docs/5code-recognition/integrations/nametags-and-chat/integration-snippets/chat-default.md) does exactly that for the default `chat` resource.

## ID cards, phones and business cards

An ID-card script that shows a name to somebody is really an **introduction**:

```lua
exports['5code-recognition']:RecordIntroduction(speakerSrc, listenerSrc, name, opts)
```

Two players and a name, nothing more. It is a **write** export, so your resource must be in `ServerConfig.Api.TrustedResources`.

The core decides what happens to that name:

* It records whether the speaker was **concealed** when the name was given.
* The name attaches to whichever memory the listener's next scan of this meeting resolves to.
* A **visible** face names the person's real memory. A **hidden** face can only ever name a *disguise* memory.

{% hint style="info" %}
**That is on purpose, and it is why the export is so small.** Your ID-card script only hands over a name; the script decides where it goes. The export gives the same neutral answer every time, so it can never be used to find out who already knows whom.
{% endhint %}

Related settings: `Config.Introduce.SameEncounterOnly` (a masked introduction only sticks through a scan of the same meeting), `Config.Introduce.PendingHours`, and `Config.Introduce.MaxPending`. See [Devices & Reports](/5code-docs/5code-recognition/configuration/devices-and-reports.md).

## Custom

Set `Config.NametagSystem = 'custom'` and fill the `'custom'` branch in `bridge/nametag_client.lua`, or register a driver at runtime with `NametagBridge.SetDriver`. A driver that keeps its own table gets an `onClear` callback so it can empty it.

See [Writing a Custom Adapter](/5code-docs/5code-recognition/integrations/custom-adapter.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://5code.gitbook.io/5code-docs/5code-recognition/integrations/nametags-and-chat.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
