# Manifest reference

> Every field of neurosquad-card.json — identity, size, permissions, the settings form, ports and tools — with the rules the app checks.

Source: https://docs.neurosquad.ai/en/card-sdk/manifest

Every card has a `neurosquad-card.json` at the root of its folder. The app reads it at install and
refuses a card whose manifest does not pass; `npx @neurosquad/card-sdk validate` runs the very same
checks on your machine.

For editor completion, point `$schema` at the schema that ships with the SDK:
`./node_modules/@neurosquad/card-sdk/schema/neurosquad-card.v1.json` (React template) or
`./vendor/neurosquad-card.schema.json` (plain template). It is also exported as
`@neurosquad/card-sdk/schema.json`.

## A complete example

```json
{
  "$schema": "./node_modules/@neurosquad/card-sdk/schema/neurosquad-card.v1.json",
  "manifestVersion": 1,
  "name": "test-radar",
  "displayName": { "en": "Test radar", "ru": "Радар тестов", "zh": "测试雷达" },
  "version": "1.0.0",
  "protocol": 1,
  "description": "Runs the test suite in a connected terminal and shows what broke.",
  "author": { "name": "Acme", "url": "https://acme.dev" },
  "license": "MIT",
  "homepage": "https://acme.dev/test-radar",
  "keywords": ["tests", "ci"],
  "icon": "icon.png",
  "minAppVersion": "0.1.100",
  "entry": "dist/index.html",
  "card": {
    "defaultSize": { "w": 460, "h": 340 },
    "minSize": { "w": 320, "h": 220 }
  },
  "permissions": [
    "agents.read",
    "terminals.write",
    { "id": "fs.read", "reason": "Reads the JUnit report" },
    { "id": "network", "hosts": ["api.github.com"], "reason": "Links failures to GitHub issues" },
    { "id": "clipboard.write", "optional": true }
  ],
  "settings": [
    { "key": "command", "type": "string", "label": "Test command", "default": "npm test" },
    { "key": "githubToken", "type": "secret", "label": "GitHub token", "scope": "package" }
  ],
  "ports": {
    "inputs": [{ "id": "run", "label": "Run", "type": "ns:trigger", "default": true }],
    "outputs": [
      { "id": "failures", "label": "Failures", "type": "ns:tasks", "retain": true },
      { "id": "summary", "label": "Summary", "type": "ns:markdown" }
    ]
  },
  "tools": [
    {
      "name": "run_tests",
      "title": "Run the tests",
      "description": "Runs the project's tests and returns failing test names with messages.",
      "inputSchema": {
        "type": "object",
        "properties": { "filter": { "type": "string", "maxLength": 200 } }
      },
      "timeoutMs": 120000
    }
  ]
}
```

## Identity

| Field | Required | Rule | Meaning |
| --- | --- | --- | --- |
| `$schema` | | string | For your editor. Ignored by the app. |
| `manifestVersion` | yes | `1` | The manifest format. |
| `name` | yes | `a-z`, digits and `-`, starts with a letter, 2–48 chars | The package slug. It prefixes the card's [tool names](https://docs.neurosquad.ai/en/card-sdk/api/tools) and namespaces its custom port types. Reserved (built-in cards and tool families): `neurosquad`, `neurosquad-card`, `custom`, `canvas`, `browser`, `terminal`, `agent`, `note`, `todo`, `kanban`, `host`, `system`, `telegram`, `image`, `reference`, `ports`, `web-watch`, `watch`, `timer`, `sticky`, `standup`, `squad`, `mcp`, `skill`, `official`. |
| `displayName` | yes | localized text, ≤ 80 | The card's name in menus, the header and the install dialog. |
| `version` | yes | SemVer (`1.2.0`, `1.2.0-beta.1`) | Informational — installs are pinned to a commit, not a version. Bump it anyway: users see it on update. |
| `protocol` | yes | integer | The card protocol you built against — `1` today. A card built for a newer protocol than the app speaks is refused with "needs a newer NeuroSquad". |
| `description` | | localized text, ≤ 300 | Shown in the install dialog and the card picker. |
| `author` | | `{ "name", "url"? }` | `url` must be `https://`. Shown as **self-declared**. |
| `license` | | string ≤ 100 | An SPDX id such as `MIT`. |
| `homepage` | | `https://` URL | |
| `keywords` | | ≤ 20 unique strings, ≤ 40 chars each | |
| `icon` | | path to a `.png` or `.webp` in the package | Square, ≤ 128 KB (128×128 or larger looks sharp). Checked by its bytes at install. Without one the card shows a puzzle piece. |
| `minAppVersion` | | SemVer | The lowest NeuroSquad version that can run the card. |
| `entry` | | path to an `.html` file, default `index.html` | The page loaded into the card. |

**Localized text** is either a plain string or an object with English required:
`{ "en": "Test radar", "ru": "Радар тестов", "zh": "测试雷达" }`. The app shows the user's language
and falls back to English.

**Text rules.** Names, descriptions, labels, reasons and tool descriptions may not contain control
characters (tab and newline are allowed in descriptions), bidi overrides or isolates, line
separators, zero-width or other invisible characters. Only official cards (published from the
`glmn-ai` organization) may call themselves "NeuroSquad", "official" or "verified" in `displayName`
or `author` — `validate` warns, the app refuses.

**Paths** (`entry`, `icon`) are relative to the manifest, with `/` separators — no `..`, no leading
`/`, no backslashes, no Windows device names (`con`, `nul`…), nothing under `__ns/` (the app reserves
it), at most 20 levels and 240 characters.

## Size — `card`

```json
"card": {
  "defaultSize": { "w": 460, "h": 340 },
  "minSize": { "w": 320, "h": 220 },
  "maxSize": { "w": 1200, "h": 900 }
}
```

Sizes are in canvas units (CSS pixels at 100 % zoom), whole numbers, width 200–2400 and height
120–1800, with `minSize ≤ defaultSize ≤ maxSize`. Defaults: 420×320, min 200×120, max 2400×1800.
The user resizes within min/max; [`card.requestResize()`](https://docs.neurosquad.ai/en/card-sdk/api/card-ui#resize) is clamped
to them too.

## Permissions — `permissions`

A list (≤ 32) of permission ids, or objects that add details:

```json
"permissions": [
  "agents.read",
  { "id": "network", "hosts": ["api.github.com", "*.example.com", "status.example.org:8443"] },
  { "id": "fs.read", "reason": { "en": "Reads the JUnit report", "ru": "Читает отчёт JUnit" } },
  { "id": "clipboard.write", "optional": true }
]
```

| Key | Meaning |
| --- | --- |
| `id` | One of the ids on [Permissions](https://docs.neurosquad.ai/en/card-sdk/permissions). |
| `hosts` | `network` only, and required there: ≤ 32 host patterns. |
| `reason` | Localized text ≤ 300, shown under the permission in the install dialog. Say why. |
| `optional` | `true` = not asked at install; the card asks at runtime with [`card.permissions.request()`](https://docs.neurosquad.ai/en/card-sdk/api/environment#permissions). |

Required permissions are all-or-nothing at install. Listing the same id twice merges it (and a
required entry wins over an optional one).

**Host patterns** for `network`: lowercase host names only — `api.example.com`,
`api.example.com:8443` (a port other than 443), or `*.example.com` (any subdomain, not
`example.com` itself). No scheme, path, IP address, bare `*` or `localhost` — this computer is the
separate `network.local` permission.

## Settings form — `settings`

Up to 40 fields. The app draws the form (open it from the card's **⋯ → Settings…** or with
[`card.settings.open()`](https://docs.neurosquad.ai/en/card-sdk/api/storage-settings#settings)); the card reads the values.

```json
"settings": [
  { "key": "command", "type": "string", "label": "Test command", "default": "npm test", "maxLength": 200 },
  { "key": "notes", "type": "text", "label": "Notes", "placeholder": "Anything the agents should know" },
  { "key": "interval", "type": "number", "label": "Check every (min)", "default": 5, "min": 1, "max": 60, "step": 1 },
  { "key": "compact", "type": "boolean", "label": "Compact view", "default": false },
  { "key": "branch", "type": "select", "label": "Branch", "default": "main",
    "options": [{ "value": "main", "label": "main" }, { "value": "dev", "label": "dev" }] },
  { "key": "accent", "type": "color", "label": "Accent", "default": "#22c55e" },
  { "key": "apiKey", "type": "secret", "label": "API key", "required": true, "scope": "package" }
]
```

| Key | Meaning |
| --- | --- |
| `key` | Starts with a letter; letters, digits, `_`, `-`; ≤ 64. Unique. |
| `type` | `string` (one line), `text` (several lines), `number`, `boolean`, `select`, `color` (`#rrggbb`), `secret`. |
| `label`, `description`, `placeholder` | Localized text (≤ 80, ≤ 300, ≤ 80). |
| `default` | Of the field's type. Must be one of the options for `select`, `#rrggbb` for `color`, within `min`/`max` for `number`. **Not allowed for `secret`.** |
| `required` | The form will not save without it. |
| `scope` | `instance` (default): per card on the canvas. `package`: shared by every card of this package. |
| `maxLength` | `string`, `text`, `secret`. Defaults: 2 000 for `string`, 20 000 for `text`. |
| `min`, `max`, `step` | `number`. |
| `options` | `select` only, 1–50 `{ "value", "label" }`. |

**Secrets** are entered only in the app's form, stored encrypted, and never reach the card: the card
only learns whether one is set, and uses it through a `{{secret:<key>}}` placeholder in
[network request headers](https://docs.neurosquad.ai/en/card-sdk/api/network#secrets).

## Ports — `ports`

Typed data connections over arrows: up to 16 `inputs` and 16 `outputs`. Full guide:
[Ports](https://docs.neurosquad.ai/en/card-sdk/api/ports).

```json
"ports": {
  "inputs": [
    { "id": "run", "label": "Run", "type": "ns:trigger", "default": true },
    { "id": "lookup", "label": "Look up", "type": "ns:text", "mode": "request",
      "response": { "type": "ns:json" } }
  ],
  "outputs": [
    { "id": "failures", "label": "Failures", "type": "ns:tasks", "retain": true },
    { "id": "coverage", "label": "Coverage", "type": "test-radar/coverage",
      "description": "Line coverage per file, 0..1",
      "schema": {
        "type": "object",
        "additionalProperties": { "type": "number", "minimum": 0, "maximum": 1 }
      } }
  ]
}
```

| Key | Applies to | Meaning |
| --- | --- | --- |
| `id` | both | `a-z`, digits, `-`, starts with a letter, ≤ 32. Unique per direction. |
| `label`, `description` | both | Localized text (≤ 80, ≤ 500). Shown to users, peer cards and agents discovering the port. |
| `type` | both | A well-known `ns:*` type or a custom `<package-name>/<type-name>`. |
| `schema` | both | Extra JSON Schema the value must also match. **Required for custom types.** |
| `mode` | inputs | `stream` (default): fire-and-forget messages. `request`: the sender waits for a reply. |
| `response` | request inputs | `{ "type", "schema"? }` — the reply's type. Required for `mode: "request"`. |
| `default` | inputs | The preferred input when a peer's output fits several. At most one. |
| `retain` | outputs | The app keeps the last value: peers can read it at any time, and a newly connected peer gets it once. |

A custom type from another package's namespace is allowed (with a warning) — that is how two cards
agree on a shared format.

## Tools — `tools`

Up to 32 tools that agents connected to the card can call. Guide: [Tools for agents](https://docs.neurosquad.ai/en/card-sdk/api/tools).

| Key | Meaning |
| --- | --- |
| `name` | `a-z`, digits, `_`, starts with a letter, ≤ 40. The agent sees `<name with - → _>_<tool>`, e.g. `test_radar_run_tests`. That exposed name may not equal a built-in NeuroSquad tool (`canvas_spawn_card`, `terminal_send_keys`…) and may not contain `__`. |
| `title` | ≤ 80, a human title. |
| `description` | ≤ 2 000, **for the model**, in English: what it does and what it returns. |
| `inputSchema` | JSON Schema with `"type": "object"`. The property `card` is reserved (the app adds it). |
| `readOnly` | `true` when the tool changes nothing — a hint shown to the agent. |
| `timeoutMs` | 1 000–120 000, default 30 000. |

## JSON Schemas you write

Port schemas, response schemas and tool `inputSchema` use a safe subset of JSON Schema 2020-12,
checked at install (≤ 500 nodes, ≤ 16 levels deep):

- **Supported:** `type`, `enum`, `const`, `properties`, `required`, `additionalProperties`,
`minProperties`, `maxProperties`, `items`, `minItems`, `maxItems`, `uniqueItems`, `minLength`,
`maxLength`, `format`, `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`, `multipleOf`,
`anyOf`, `oneOf`, `$ref` (`#` or `#/$defs/<name>`), `$defs`.
- **Formats:** `uri`, `date-time`, `date`, `email`, `color`, `uuid`.
- **Ignored annotations:** `$id`, `$schema`, `$comment`, `title`, `description`, `default`,
`examples`, `deprecated`, `readOnly`, `writeOnly`.
- **Caps:** `enum` ≤ 256 options and ≤ 16 KB in total; `const` ≤ 4 KB.
- **Not allowed:** `pattern` (regular expressions from a card would run in the app — a denial of
service risk), and any keyword not listed above.

> The schema's `$id`, `https://neurosquad.ai/schemas/neurosquad-card.v1.json`, is an identifier, not
> a download: point `$schema` at the copy in the SDK.
