Documentation
¶
Overview ¶
Package nativehost implements the Chrome Native Messaging host the companion browser extension pairs through: Chrome launches `ogcode native-host` with the port's stdin/stdout bound, and everything the extension needs to find this machine's running ogcode servers — an origin, a websocket URL and a pair token per server — is one "pair" request away.
The wire protocol is the one Chrome defines for native messaging: every message, request and reply alike, is a 4-byte length followed by that many bytes of UTF-8 JSON. Chrome's spec says the length is written in the machine's native byte order, and every platform Chrome ships on is little-endian (x86, ARM64, WebAssembly), so binary.LittleEndian matches native order everywhere while keeping the framing platform-independent.
Only framed messages may ever reach the host's stdout — Chrome reads that pipe as a stream of frames, and one stray line breaks every reply after it — so diagnostics go to stderr through slog and never touch out.
Index ¶
- Constants
- func CandidatesFor(projectDir string) []string
- func DedupeExtensionIDs(ids []string) []string
- func DisabledByEnv() bool
- func EnsureRegistered(projectDir string) error
- func ExtensionIDForPath(path string) string
- func ManifestPaths() []string
- func Register(executablePath string, extensionIDs []string) error
- func Run(in io.Reader, out io.Writer) error
- func Uninstall() error
- func UnpackedIDFor(projectDir string) (string, bool)
Constants ¶
const ExtensionIDsEnv = "OGCODE_EXTENSION_IDS"
ExtensionIDsEnv is the process-wide env var of extra extension ids to allowlist, comma-separated.
const NativeHostName = "com.ogcode"
NativeHostName is the native-messaging host's name: it names both the manifest file Chrome looks up ({Name}.json) and the chrome.runtime.connectNative call the extension makes.
const UnpackedExtensionDir = "browser-extension"
UnpackedExtensionDir names the project folder the companion extension lives in while it is built, before the store publishes an id for it.
const WebStoreExtensionID = "kacnfgnhmmnhdpokflkiemdehhjhkidp"
WebStoreExtensionID is the companion extension's published Chrome Web Store id. Store installs run under this id, so every native-messaging manifest written at boot allowlists it and a store install pairs itself without a flag. (Unpacked repo installs keep their path-derived id alongside it.)
Variables ¶
This section is empty.
Functions ¶
func CandidatesFor ¶
CandidatesFor lists the extension ids projectDir is entitled to allowlist: the unpacked browser-extension/ directory when the project ships one, ids named in OGCODE_EXTENSION_IDS, and the published web store id once the extension ships. Deduplicated in order, empty ids dropped.
func DedupeExtensionIDs ¶
DedupeExtensionIDs keeps each id's first occurrence, in order, dropping empty strings — the shape CandidatesFor returns and Register accepts.
func DisabledByEnv ¶
func DisabledByEnv() bool
DisabledByEnv reports whether OGCODE_NATIVE_HOST_INSTALL is set to a falsey value (0/false/no/off). It gates only the server's boot-time auto-install: an explicit `ogcode native-host install` runs regardless, and the way to refuse manifests for good is `native-host uninstall`.
func EnsureRegistered ¶
EnsureRegistered is the server's boot-time install: when this project entitles any extension id, keep every installed browser's manifest pointing at this ogcode executable, so a fresh extension install pairs itself without a manual paste. Idempotent — repeated boots rewrite the same bytes — and a no-op when there is nothing to allowlist. OGCODE_NATIVE_HOST_INSTALL falsey turns the whole thing off for a deployment that wants the manifests untouched.
func ExtensionIDForPath ¶
ExtensionIDForPath computes the extension id Chrome assigns to an unpacked extension loaded from path: SHA-256 over the absolute path's UTF-8 bytes, the first 16 digest bytes, each rendered in the a–p alphabet (high nibble, then low) as one 32-character id. It mirrors Chromium's GenerateIdForPath, which hashes the path AS SPELLED — verified against Chrome 156.0.8078.48 on macOS 15.5, which recorded the sha256 of the mixed-case path verbatim. The lowercased spelling this file hashed until 2026-10-25 produced an id Chrome never assigned, so the boot-written manifest allowlisted an origin no installed extension could launch the host from.
func ManifestPaths ¶
func ManifestPaths() []string
ManifestPaths lists the manifest file paths registration would write right now — the supported browsers whose profile folder exists — so the CLI can report exactly what an install touches (and an uninstall clean up).
func Register ¶
Register writes the native-messaging manifest naming executablePath into every supported browser's NativeMessagingHosts directory whose browser profile folder exists — a browser the machine has never run installs nothing. It is idempotent: the same inputs rewrite the same file. Existing manifests of this host are overwritten; another host's file is left alone.
func Run ¶
Run serves the native-messaging protocol over in/out with the production prober: every recorded port is probed over loopback HTTP at pair time.
func Uninstall ¶
func Uninstall() error
Uninstall removes every manifest this host wrote, wherever it found one. It is idempotent — running it with nothing installed succeeds.
func UnpackedIDFor ¶
UnpackedIDFor returns the id Chrome would assign to projectDir's unpacked companion, and whether such an extension (a manifest.json) is present there at all — the two facts the CLI's install hint names.
Types ¶
This section is empty.