Codium nostr extension
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
.mdfile 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
nsecor hex key; held in memory only, never written to disk. - n_signer (Unix socket) — delegate signing to a local
n_signerprocess over an abstract Unix socket. Your key never leaves the signer. - n_signer (Qubes qrexec) — reach
n_signerin another qube via Qubes OS secure IPC. - n_signer (TCP) — reach
n_signerover TCP (FIPS mesh, remote machine). Requires a caller key for auth envelopes.
- Local — enter your
- 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_attracking — 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-toolscloned 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
nsignerbinary 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. Requiresnsigner --listen unix --socket-name nsignerrunning on the same machine. -
n_signer (Qubes qrexec) — enter the target qube name (e.g.
nostr_signer) and key index. Requires thequbes.NsignerRpcservice 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. Requiresnsigner --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
-
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. -
Edit the front-matter (metadata) and write your article body in Markdown below the
---line. -
Click Validate Front-Matter to preview the metadata that will be published (dry run — nothing is sent).
-
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
- Click + New Event → pick a kind (Short Text Note, Relay List, Profile, Contacts, or Custom).
- A JSON skeleton opens with the expected tags and placeholder content for that kind.
- Fill in the
contentandtagsfields. - Click Publish Current JSON File (or right-click in the JSON editor → Nostr: Publish Event from JSON File).
- 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.NsignerRpcservice in the target qube. - Install dom0 qrexec policy allowing your qube to call it.
- The extension spawns
qrexec-client-vm <qube> qubes.NsignerRpcper 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 —
imageURLs 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_atworkspace-state map is per-workspace; switching workspaces loses the first-publish timestamps. - Serial transport (USB CDC) is not supported — needs a native
serialportmodule. Use unix, qrexec, or tcp instead.
Design docs
Detailed design documents are in plans/:
plans/phase0-raw-event-publishing.md— raw event publishing.plans/longform-metadata-strategy.md— YAML front-matter + NIP-23 metadata.plans/implementation-plan.md— full implementation plan with code sketches.
License
MIT
Write a comment