215 lines
11 KiB
Markdown
215 lines
11 KiB
Markdown
---
|
|
slug: codium-nostr-extension
|
|
title: Codium nostr extension
|
|
summary: A Codium / VSCode extension to publish files to [Nostr].
|
|
image:
|
|
tags: [nostr, writing, codium, vscode]
|
|
---
|
|
|
|
# Nostr Publish
|
|
|
|
A Codium / VSCode extension to publish files to [Nostr](https://github.com/nostr-protocol/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`](https://laantungir.net/git/laantungir/n_signer.git) 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:
|
|
|
|
```bash
|
|
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`](https://laantungir.net/git/laantungir/n_signer.git) binary installed and running. See the n_signer README for build instructions.
|
|
|
|
### Build and install
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
npm run compile
|
|
npx vsce package --no-dependencies
|
|
codium --install-extension nostr-publish-0.1.0.vsix --force
|
|
```
|
|
|
|
### Updating after code changes
|
|
|
|
```bash
|
|
./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:
|
|
|
|
```markdown
|
|
---
|
|
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):
|
|
```bash
|
|
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):
|
|
```bash
|
|
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](https://laantungir.net/git/laantungir/n_signer.git) 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/`](plans/):
|
|
- [`plans/phase0-raw-event-publishing.md`](plans/phase0-raw-event-publishing.md) — raw event publishing.
|
|
- [`plans/longform-metadata-strategy.md`](plans/longform-metadata-strategy.md) — YAML front-matter + NIP-23 metadata.
|
|
- [`plans/implementation-plan.md`](plans/implementation-plan.md) — full implementation plan with code sketches.
|
|
|
|
## License
|
|
|
|
MIT
|