skills/nsyte/SKILL.md
Deploy and manage censorship-resistant static websites on the Nostr network using nsyte CLI. Covers installation, project setup, configuration, authentication (NIP-46 bunkers), deployment to Blossom servers, CI/CD automation, and troubleshooting. Use when the user mentions nsyte, nsite, Nostr site hosting, Blossom uploads, or decentralized web publishing.
npx skillsauth add sandwichfarm/nsyte nsyteInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
nsyte is a Deno-based CLI that publishes static websites to the Nostr network using Blossom servers for file storage. Sites are content-addressed (SHA-256), censorship-resistant, and discoverable via Nostr relays.
Key concepts: For Nostr/Blossom domain knowledge (relays, pubkeys, nsec, NIP-46, events), see references/nostr-concepts.md.
nsyte --version
If this prints a version string, skip to Project Setup.
curl -fsSL https://nsyte.run/get/install.sh | bash
Installs to /usr/local/bin/nsyte. Use sudo if that directory is not writable.
deno install -A -f -g -n nsyte jsr:@nsyte/cli
Download the latest binary from https://github.com/sandwichfarm/nsyte/releases, place in
%USERPROFILE%\bin\, and add that directory to PATH.
/usr/local/bin to PATH in ~/.bashrc or ~/.zshrc and reload.sudo for the curl install, or install to a user-writable location.deno upgrade or use the binary install instead.cd /path/to/your/project
nsyte init
Interactive prompts ask for:
wss:// relay WebSocket URLshttps:// Blossom server URLsOn success, creates .nsite/config.json. See Configuration for the schema.
Config file: .nsite/config.json
For the full JSON Schema, see assets/config.schema.json.
| Field | Type | Description |
| --------- | ---------- | ---------------------------------------------- |
| relays | string[] | Nostr relay URLs (wss://), unique items |
| servers | string[] | Blossom server URLs (https://), unique items |
| Field | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
| bunkerPubkey | string | 64-char hex pubkey for NIP-46 bunker. Pattern: ^[0-9a-fA-F]{64}$. Always set via nsyte bunker use, never manually. |
| Field | Type | Default | Description |
| ------------- | ---------------- | ------- | -------------------------------------------------------------------------- |
| id | string \| null | null | null/empty = root site (kind 15128). Non-empty = named site (kind 35128) |
| title | string | — | Site title for manifest event |
| description | string | — | Site description for manifest event |
| fallback | string | — | 404 fallback HTML path (e.g., /index.html for SPAs) |
id must be null/empty)| Field | Type | Description |
| ------------------- | --------- | -------------------------------------------------------------------- |
| publishProfile | boolean | Publish kind 0 profile metadata. Requires non-empty profile object |
| publishRelayList | boolean | Publish kind 10002 relay list |
| publishServerList | boolean | Publish kind 10063 Blossom server list |
| publishAppHandler | boolean | Publish NIP-89 handler. Requires appHandler with kinds |
| Field | Type | Description |
| ------------------ | ---------- | --------------------------------------------------------------------------------------------------------- |
| profile | object | Nostr profile: name, display_name, about, picture, banner, website, nip05, lud16, lud06 |
| appHandler | object | NIP-89 config: kinds (required), id, name, description, icon, platforms |
| gatewayHostnames | string[] | Gateway hostnames (default: ["nsite.lol"]) |
Minimal (root site):
{
"relays": ["wss://relay.damus.io", "wss://nos.lol"],
"servers": ["https://cdn.hzrd149.com"]
}
Named site (blog):
{
"relays": ["wss://relay.damus.io"],
"servers": ["https://cdn.hzrd149.com"],
"id": "blog",
"title": "My Blog",
"description": "A blog about decentralized applications"
}
With profile publishing:
{
"relays": ["wss://relay.damus.io", "wss://nos.lol"],
"servers": ["https://cdn.hzrd149.com"],
"publishProfile": true,
"publishRelayList": true,
"publishServerList": true,
"profile": {
"name": "Alice",
"display_name": "Alice",
"about": "Decentralization enthusiast",
"picture": "https://example.com/avatar.jpg",
"nip05": "[email protected]",
"lud16": "[email protected]"
}
}
With NIP-89 app handler:
{
"relays": ["wss://relay.damus.io"],
"servers": ["https://cdn.hzrd149.com"],
"publishAppHandler": true,
"appHandler": {
"kinds": [1, 30023],
"name": "My Nostr Viewer",
"description": "A viewer for notes and articles",
"icon": "https://example.com/logo.png"
}
}
nsyte config
Requires an interactive terminal. Keys: ↑/↓ navigate, Enter edit, s save, r reset, q
quit.
For non-interactive contexts, edit .nsite/config.json directly and validate:
nsyte validate
nsyte bunker connect
Choose "Scan QR Code", enter a relay URL, scan with signer app (Amber, nsec.app), approve.
nsyte bunker connect 'bunker://pubkey?relay=wss://relay.example.com&secret=xxx'
CRITICAL: Always single-quote the URL — ? and & are shell metacharacters.
nsyte bunker use [pubkey]
Sets bunkerPubkey in config and stores nbunksec in OS keychain. Never manually edit
bunkerPubkey.
| Command | Purpose |
| ----------------------------------- | --------------------------------- |
| nsyte bunker connect | Connect interactively (QR or URL) |
| nsyte bunker connect '<url>' | Connect via bunker URL |
| nsyte bunker import nbunksec1... | Import existing nbunksec |
| nsyte bunker export [pubkey] | Export stored bunker as nbunksec |
| nsyte bunker list | List stored bunkers |
| nsyte bunker use [pubkey] | Set project to use a bunker |
| nsyte bunker remove [pubkey] | Remove a bunker from storage |
| nsyte bunker migrate [pubkeys...] | Rebuild keychain index |
nsyte auto-selects the best backend:
Override with NSYTE_FORCE_ENCRYPTED_STORAGE=true.
nsyte deploy ./dist
--sec flag (highest priority):
nsyte deploy ./dist --sec "nsec1..."
nsyte deploy ./dist --sec "${NBUNK_SECRET}"
Auto-detects format: nsec1..., nbunksec1..., bunker://..., or 64-char hex.
Stored bunker from .nsite/config.json bunkerPubkey + OS keychain.
If neither available, nsyte exits with an error.
| Flag | Purpose |
| ------------------------ | ------------------------------- |
| --sec <value> | Provide signing key/credential |
| --force | Re-upload all files (skip diff) |
| --fallback=/index.html | SPA fallback for 404s |
| --non-interactive | CI mode — no prompts, fail fast |
{N} files uploaded successfully ({size}) — all files to all servers{uploaded}/{total} files uploaded — some servers failedFailed to upload any files — check relay/server/auth errorsGateway URL printed after deploy: https://{npub}.nsite.lol/
nsyte ci
# or with bunker URL:
nsyte ci 'bunker://pubkey?relay=wss://relay.example.com&secret=xxx'
The nbunksec1... string is printed once and never stored. Copy it immediately and save as a CI
secret.
nsyte deploy ./dist --non-interactive --sec "${NBUNK_SECRET}"
CRITICAL: Use --sec (not --nbunksec).
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: denoland/setup-deno@v1
- run: nsyte deploy ./dist --non-interactive --sec "${{ secrets.NBUNK_SECRET }}"
.nsite/config.json committed or provided as artifactNBUNK_SECRET secret set to nbunksec1... string--non-interactive flag presentnsyte validate before deploy| Command | Purpose |
| ---------------------- | ------------------------------------------------------------------- |
| nsyte ls | List published files |
| nsyte browse | Interactive TUI file browser with relay/server propagation tracking |
| nsyte download <dir> | Download published files |
| nsyte serve -d <dir> | Local dev server |
| nsyte run | Start resolver server with npub subdomains |
| nsyte debug <npub> | Debug nsite setup (relays, servers, integrity) |
| nsyte validate | Validate config (exit 0 = valid, 1 = invalid) |
| nsyte purge | Remove published files from relays/servers |
| nsyte sites | List available sites (root + named) |
--sec or configure bunker via nsyte bunker use.bunkerPubkey set in config but keychain entry missing. Fix:
nsyte bunker use [pubkey].'bunker://...'.relays array in config has valid wss:// URLs. Try
--use-fallback-relays.servers array. Try adding a different server via
nsyte config.nsyte init first.nsyte validate.publishAppHandler: true without
appHandler.kinds, publish* on named site.nsyte bunker use.tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.