Chio/Docs
LOGIN · JOIN

Editor Integrations

Build the Chio language server, connect VS Code or Zed to it, and see a chio.yaml diagnostic match chio doctor.

Guide chio@5f63e6e

On this page

chio-lsp flags a policy or manifest mistake while you type it, inside VS Code, Zed, or any editor that speaks the Language Server Protocol. Its diagnostics carry the same registry codes as chio doctor.

Both extensions are thin clients: they spawn chio-lsp and forward edits and requests. The server owns the document cache, validation, and the completion, hover, and definition catalogs, so every editor gets the same answers.

Start

1. Check the language server

The installer puts chio-lsp on your PATH beside chio. The editor needs no running kernel, receipt store, or trust-control service.

Terminalbash
chio-lsp --version
Output
chio-lsp 0.2.0

You should see the same version as chio --version. If the shell cannot find it, rerun the installer, or pin the binary's absolute path in step 3.

2. Load the extension for your editor

For VS Code, install the Chio extension from the marketplace, or search for Chio in the Extensions view.

Terminalbash
code --install-extension backbay.vscode-chio
Output
Installing extensions...
Extension 'backbay.vscode-chio' v0.2.0 was successfully installed.

For Zed, run zed: extensions from the command palette, search for Chio, and install it.

You should see Chio language support activate when you open a matching file such as chio.yaml. If the editor treats the file as plain YAML, select the Chio language mode.

3. Point the editor at the binary

Both editors resolve chio-lsp on PATH and spawn it with no arguments. Pin an absolute path when the binary lives elsewhere.

settings.jsonsettings.json
{
  "chio.lsp.path": "/opt/chio/bin/chio-lsp",
  "chio.lsp.args": [],
  "chio.trace.server": "off"
}

The server speaks LSP over stdio and takes no command-line options, so leave the arguments empty. In Zed an empty or whitespace-only path falls back to the bare chio-lsp on PATH.

4. Open an incomplete chio.yaml and see a diagnostic

In a scratch directory, create a chio.yaml that omits the required policy key.

chio.yamlyaml
version: 1
capabilities: []

You should see an error from chio-lsp with the code urn:chio:error:cli:doctor-chio-yaml-invalid and the message that policy is missing. A missing key has no token to point at, so the caret sits at the start of the document. If nothing appears, check the filename, the language mode, and the binary path from step 3.

Probe the server without an editor

To separate a server problem from an editor problem, this Python 3 probe sends framed JSON-RPC over stdio, the transport the extensions use. It runs the binary named in CHIO_LSP, or chio-lsp from PATH.

lsp-probe.pypython
import json, os, subprocess, sys, time

def frame(msg):
    body = json.dumps(msg).encode()
    return b"Content-Length: %d\r\n\r\n%s" % (len(body), body)

def replies(messages):
    server = os.environ.get("CHIO_LSP", "chio-lsp")
    proc = subprocess.Popen([server], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    for msg in messages:
        proc.stdin.write(frame(msg))
        proc.stdin.flush()
        time.sleep(0.5)
    proc.stdin.close()
    raw, out = proc.stdout.read().decode(), []
    while "Content-Length: " in raw:
        head = raw.index("Content-Length: ")
        gap = raw.index("\r\n\r\n", head)
        size = int(raw[head + 16 : gap])
        out.append(json.loads(raw[gap + 4 : gap + 4 + size]))
        raw = raw[gap + 4 + size :]
    return out

OPEN = {"jsonrpc": "2.0", "id": 1, "method": "initialize",
        "params": {"processId": None, "rootUri": None, "capabilities": {}}}
READY = {"jsonrpc": "2.0", "method": "initialized", "params": {}}
BAD_CONFIG = {"jsonrpc": "2.0", "method": "textDocument/didOpen", "params": {"textDocument": {
    "uri": "file:///work/chio.yaml", "languageId": "chio-yaml", "version": 1,
    "text": "version: 1\ncapabilities: []\n"}}}

if sys.argv[1] == "handshake":
    for reply in replies([OPEN]):
        if "result" in reply:
            print(json.dumps(reply["result"], indent=2))
else:
    for reply in replies([OPEN, READY, BAD_CONFIG]):
        if reply.get("method") == "textDocument/publishDiagnostics":
            print(json.dumps(reply["params"]["diagnostics"], indent=2))

A working server answers initialize with four capabilities and its name and version.

python3 lsp-probe.py handshaketranscript
$ python3 lsp-probe.py handshake
{
  "capabilities": {
    "completionProvider": {
      "triggerCharacters": [
        ":",
        " ",
        "-"
      ]
    },
    "definitionProvider": true,
    "hoverProvider": true,
    "textDocumentSync": 1
  },
  "serverInfo": {
    "name": "chio-lsp",
    "version": "0.1.0"
  }
}
exit 0

textDocumentSync: 1 is LSP full sync: every edit resends the whole document, so the server validates from its cache without reading disk. Completions trigger on ":", " ", and "-", after a key and inside a list item. Diagnostics are pushed through textDocument/publishDiagnostics when a document opens or changes; the reply advertises no pull-diagnostics provider.

python3 lsp-probe.py diagnostictranscript
$ python3 lsp-probe.py diagnostic
[
  {
    "code": "urn:chio:error:cli:doctor-chio-yaml-invalid",
    "message": "chio.yaml is missing required key `policy`.",
    "range": {
      "end": {
        "character": 0,
        "line": 0
      },
      "start": {
        "character": 0,
        "line": 0
      }
    },
    "severity": 1,
    "source": "chio-lsp"
  }
]
exit 0

An editor renders three fields: code is the registry URN, source is chio-lsp, and severity: 1 is the LSP error level. A parse error carries the line and column the YAML parser reported.

5. Scaffold a manifest from a snippet

In a new app.chio-manifest.yaml, type manifest-skeleton and expand it.

app.chio-manifest.yamlyaml
version: 1
name: manifest_name
policy:
  default: deny
capabilities:
  - id: capability_id
    scope:
      - scope.action
guards:
  - ref: guard.identifier

You should see a complete starting manifest with policy, capabilities, and guard references filled in with default values. Snippets use LSP-standard ${1:label} placeholders, so both editors accept the same source. Three more prefixes are listed in the reference below.

6. Confirm chio doctor reports the same code

Run chio doctor in the directory holding the incomplete chio.yaml from step 4.

chio doctor on the same filetranscript
$ chio doctor --skip-network | grep -E 'chio_yaml|missing:'
  [error] chio_yaml (urn:chio:error:cli:doctor-chio-yaml-invalid): ~/chio/chio.yaml is missing required keys: policy
    missing: policy
exit 1

You should see urn:chio:error:cli:doctor-chio-yaml-invalid and missing: policy, the code your editor showed. The editor and the CLI check the same required keys, version and policy, and draw their codes from one registry.

A silent editor has not reached the server

When a Chio file shows no diagnostics, either the client failed to spawn chio-lsp or the server classified the document as something it leaves alone.

No diagnostics, completions, or hover in any file

The client could not spawn the server. Run the handshake probe from step 4; if it fails too, the binary is missing from PATH. Pin an absolute path in chio.lsp.path or in Zed's binary.path.

The server starts, but one file gets no diagnostics

That document classified as Other. The server takes the reported language id first and the path suffix second, so a file named anything other than chio.yaml, *.chio-manifest.yaml, or *.chio-guard.yaml is cached and left unchecked. Rename it, or select the Chio language mode by hand.

Editor and language-server reference

Extensions

EditorPackageIdentity
Visual Studio Code
vscode-chio
Publisher backbay, display name Chio, Apache-2.0
Zed
zed-chio
Extension id chio, language server Chio Language Server

Both use the same language server and the same authoring snippets. Neither bundles chio-lsp; each spawns the binary the installer put on your PATH.

Supported documents

Language IDMatchesPurpose
chio-yaml
chio.yaml
Project config: policy reference, capabilities, guards, manifest path
chio-manifest
*.chio-manifest.yaml
Tool and capability manifest
chio-guard
*.chio-guard.yaml
Guard DSL

A chio.yaml points at a HushSpec policy file or inline block, lists capabilities, and activates guards. Manifests describe tools and capabilities; guard DSL files describe guard pipelines. VS Code contributes the three language IDs; Zed contributes a single Chio language scoped to the same suffixes (chio.yaml, chio-manifest.yaml, chio-guard.yaml).

VS Code settings

SettingDefaultDescription
chio.lsp.path
chio-lsp
Path to the server binary; resolved on PATH when left as the default
chio.lsp.args
[]
Extra arguments forwarded to the server verbatim on spawn
chio.trace.server
off
LSP trace verbosity: off, messages, or verbose

Zed settings

Zed reads the standard lsp.<server>.binary block with path and arguments. The server id is chio-lsp.

Language-server features

chio-lsp classifies each open document by language ID or file suffix and serves four request classes from a shared document cache. It reads only the documents the editor sends; it runs no kernel and evaluates no policy.

FeatureDocumentsBehavior
Diagnostics
all three
Required-key and shape checks over parsed YAML on didOpen and didChange, one provider per language
Completion
chio.yaml only
Capability scopes under scopes, tools, capabilities; guard identifiers under guards; top-level keys
Hover
all three
Registry help on urn:chio:scope:*, urn:chio:guard:*, and urn:chio:error:* identifiers
Go to definition
all three
Resolves a urn:chio:scope:* or urn:chio:guard:* reference to a linked manifest, then to its first occurrence in the open document

The completion catalog offers the chio.yaml top-level keys version, policy, capabilities, guards, and manifest; version and policy are required. Manifests and guard documents get diagnostics, hover, and go-to-definition. The capability and guard catalogs are curated: a starting vocabulary, independent of what your project declares.

Diagnostics and registry codes

Every diagnostic carries a registry code in its LSP code field, formatted urn:chio:error:<domain>:<code>, for example urn:chio:error:capability:scope-mismatch. The source is chio-lsp. Editors render the URN unchanged, and downstream tooling matches on code. Every range is zero-width, so an editor renders a caret: a parse error at the reported line and column, a structural complaint with no token at the start of the document.

The CLI and the runtime emit the same registry codes. Schemas & Errors holds the registry behind both the diagnostic codes and their hover text.

Snippets and the drift check

PrefixScaffoldScope
capability-allowlist
Capability allowlist with explicit scope bounds
chio-yaml, chio-manifest
scope-bound
Scope-bound capability fragment with TTL and tenant restriction
chio-yaml, chio-manifest
guard-pipeline
Guard pipeline composing input, decision, and output stages
chio-yaml, chio-guard
manifest-skeleton
Minimal manifest with policy, capabilities, and guard references
chio-manifest

Both extensions ship the same four snippets, rendered from one tool-neutral source per snippet. Contributors covers how those sources are regenerated and checked.

Other editors

Any LSP-capable editor (Neovim, Helix, JetBrains, Emacs lsp-mode) can use chio-lsp directly:

  • Binary. chio-lsp, resolved on PATH by default, pinnable to an absolute path.
  • Transport. LSP over stdio, with no environment variables required.
  • Selectors. chio.yaml, *.chio-manifest.yaml, and *.chio-guard.yaml.
  • Diagnostics. Read the urn:chio:error:* code from each diagnostic's code field and render it as is.

Next: write the policy your editor checks