Codium nostr extension

A Codium / VSCode extension to publish files to [Nostr].

Nostr Publish

A Codium / VSCode extension to publish files to Nostr.

Publish markdown articles as long-form notes, broadcast raw Nostr events from JSON, and sign everything with either a local key or a remote n_signer hardware signer.

Features

  • Publish markdown as long-form notes — Write a .md file with YAML front-matter (title, summary, image, tags), click one button, and it’s broadcast as a NIP-23 kind-30023 event to your relays.
  • Publish raw events from JSON — Create a Nostr event skeleton (text note, relay list, profile, contacts, or custom kind), fill in the fields, and publish.
  • Multiple signing backends:
    • Local — enter your nsec or hex key; held in memory only, never written to disk.
    • n_signer (Unix socket) — delegate signing to a local n_signer process over an abstract Unix socket. Your key never leaves the signer.
    • n_signer (Qubes qrexec) — reach n_signer in another qube via Qubes OS secure IPC.
    • n_signer (TCP) — reach n_signer over TCP (FIPS mesh, remote machine). Requires a caller key for auth envelopes.
  • Sidebar GUI — sign in, manage relays, create events, and publish — all from the Nostr sidebar in the Activity Bar.
  • NIP-65 relay auto-discovery — on sign-in, fetches your kind 10002 relay list and uses your own relays for publishing.
  • Avatar display — fetches your kind 0 profile and shows your avatar + name in the sidebar.
  • published_at tracking — first-publish timestamps are stored per-workspace so updates preserve the original timestamp (NIP-23).

Install

Prerequisites

  • Codium or VSCode (≥ 1.85).

  • Node.js ≥ 18 (for building only — not needed at runtime).

  • The laantungir fork of nostr-tools cloned as a sibling directory and built:

    git clone git@laantungir.net:laantungir/nostr-tools.git ../nostr-tools
    cd ../nostr-tools
    npm install --ignore-scripts
    rm -rf lib && bun run build.js   # or: node build.js
    npx tsc                          # produces lib/types/*.d.ts
    
  • For n_signer backends (optional): the nsigner binary installed and running. See the n_signer README for build instructions.

Build and install

git clone git@laantungir.net:laantungir/nostr-publish.git
cd nostr-publish
npm install
./install_upgrade.sh

The install_upgrade.sh script compiles the extension, packages it into a .vsix, and installs it into Codium (or VSCode) with --force. Restart your editor after it finishes.

Alternatively, build and install manually:

npm run compile
npx vsce package --no-dependencies
codium --install-extension nostr-publish-0.1.0.vsix --force

Updating after code changes

./install_upgrade.sh
# restart Codium

The script handles everything — rebuild, repackage, reinstall.

Usage

1. Open the Nostr sidebar

Click the Nostr icon (broadcast glyph) in the Activity Bar on the left side of Codium. The sidebar has three sections:

  • Actions — create events, publish files, validate.
  • Relays — toggle which relays to publish to, fetch your NIP-65 relay list.
  • Identity — sign in, view your avatar/npub, sign out.

2. Sign in

In the Identity section, choose a sign-in method from the dropdown:

  • Local (nsec / hex key) — paste your nsec1... or 64-char hex secret key. The key is held in memory only for the session — it’s never written to disk. Sign out to clear it.

  • n_signer (Unix socket) — enter the abstract socket name (default nsigner) and key index. Requires nsigner --listen unix --socket-name nsigner running on the same machine.

  • n_signer (Qubes qrexec) — enter the target qube name (e.g. nostr_signer) and key index. Requires the qubes.NsignerRpc service installed in the target qube and dom0 policy allowing your qube to call it.

  • n_signer (TCP) — enter host, port, your caller nsec (for auth envelopes), and key index. Requires nsigner --listen tcp:<host>:<port> running on the target machine.

Click Sign In. The extension connects to the signer, fetches your public key, displays your avatar + npub, and auto-loads your NIP-65 relay list.

3. Publish a markdown article

  1. Click + New Event → pick 30023 — Long-Form Article. A markdown file opens with a YAML front-matter skeleton:

    ---
    slug: my-article-slug
    title: My Article Title
    summary: A one-sentence abstract for feeds.
    image: https://example.com/cover.png
    tags: [nostr, writing]
    ---
    
    # My Article Title
    
    Write your article in Markdown here.
    
  2. Edit the front-matter (metadata) and write your article body in Markdown below the --- line.

  3. Click Validate Front-Matter to preview the metadata that will be published (dry run — nothing is sent).

  4. Click Publish Current File as Long-Form Note. A confirmation dialog shows the event preview (slug, title, summary, image, tags, published_at, body length, pubkey, relays). Toggle relays on/off, then click Publish.

The front-matter block is stripped before publishing — readers on Nostr see only the markdown body. The file on disk keeps the front-matter for re-publishing.

Re-publishing an update: edit the file and publish again. The same slug (d-tag) means it’s a parameterized-replaceable update — relays replace the old version. The published_at timestamp is preserved from the first publish (tracked per-workspace).

4. Publish a raw event from JSON

  1. Click + New Event → pick a kind (Short Text Note, Relay List, Profile, Contacts, or Custom).
  2. A JSON skeleton opens with the expected tags and placeholder content for that kind.
  3. Fill in the content and tags fields.
  4. Click Publish Current JSON File (or right-click in the JSON editor → Nostr: Publish Event from JSON File).
  5. Confirm in the dialog, then broadcast.

The JSON file is a plain Nostr EventTemplate — interoperable with any other Nostr tooling that accepts EventTemplate JSON.

5. Manage relays

  • The Relays section shows your configured relays with checkboxes. Toggle individual relays on/off for the next publish.
  • Click Fetch My Relays (NIP-65) to load your kind 10002 relay list from the network and replace the sidebar’s relay set with it.
  • Default relays: wss://relay.damus.io, wss://relay.primal.net, wss://laantungir.net/relay. Change them in Codium Settings → nostr.relays.

Configuration

Setting Default Description
nostr.relays ["wss://relay.damus.io", "wss://relay.primal.net", "wss://laantungir.net/relay"] Relays to publish to.
nostr.publish.confirm true Show a confirmation dialog before broadcasting.
nostr.publish.timeoutMs 10000 Per-relay publish timeout in milliseconds.
nostr.signerBackend "local" "local" or "nsigner".
nostr.nsigner.transport "unix" "unix", "qrexec", or "tcp".
nostr.nsigner.socketName "nsigner" Abstract socket name (without @). Used when transport=unix.
nostr.nsigner.nostrIndex 0 Key index (NIP-06 m/44'/1237'/N'/0/0).
nostr.nsigner.qrexecQube "nostr_signer" Target qube name. Used when transport=qrexec.
nostr.nsigner.qrexecService "qubes.NsignerRpc" Qrexec service name.
nostr.nsigner.tcpHost "127.0.0.1" TCP host. Used when transport=tcp.
nostr.nsigner.tcpPort 8080 TCP port. Used when transport=tcp.
nostr.nsigner.callerNsec "" Caller nsec for TCP auth envelopes. Required for transport=tcp if not signed in locally.

Commands

Command Description
Nostr: Sign In Sign in with a local nsec/hex key (InputBox prompt).
Nostr: Sign Out Clear the session key.
Nostr: Create Unsigned Event Generate a kind-specific event skeleton (JSON or markdown).
Nostr: Validate Event File Validate the active JSON file as a Nostr event.
Nostr: Publish Event from JSON File Sign and publish the active JSON file.
Nostr: Publish Current File as Long-Form Note Parse front-matter, build a NIP-23 event, sign, and publish.
Nostr: Validate Front-Matter Dry-run preview of the front-matter metadata.
Nostr: Fetch My Relays (NIP-65) Fetch your kind 10002 relay list.
Nostr: Select Signer Backend Switch between local and n_signer backends (command-palette flow).
Nostr: Reset Published Timestamps Clear the local published_at tracking map.

n_signer setup

For the n_signer backends, you need a running nsigner process. The extension connects to it; it doesn’t bundle the binary.

Unix socket (local, same machine):

nsigner --listen unix --socket-name nsigner

Qubes qrexec (inter-qube):

  • Install the qubes.NsignerRpc service in the target qube.
  • Install dom0 qrexec policy allowing your qube to call it.
  • The extension spawns qrexec-client-vm <qube> qubes.NsignerRpc per call.

TCP (remote / FIPS mesh):

nsigner --listen tcp:[::]:8080

The extension signs a kind-27235 auth envelope per request with your caller key. The first request from a new caller prompts for approval at the n_signer terminal.

See the n_signer README for full build and setup instructions.

Known limitations

  • No key persistence for the local backend — the secret key must be re-entered each session (by design; n_signer backends hold the key material instead).
  • No image upload — image URLs in front-matter must be hosted externally (future Blossom/NIP-96 phase).
  • n_signer backends are Linux-only (abstract Unix sockets, qrexec, and TCP auth envelopes are Linux/Qubes primitives).
  • The published_at workspace-state map is per-workspace; switching workspaces loses the first-publish timestamps.
  • Serial transport (USB CDC) is not supported — needs a native serialport module. Use unix, qrexec, or tcp instead.

Design docs

Detailed design documents are in plans/:

License

MIT


Write a comment