nappup
v2.3.12
Published
Nostr App Uploader
Downloads
781
Readme
Napp Up!
_ _ _ _ _
| \ | | __ _ _ __ _ __ | | | |_ __ | |
| \| |/ _` | '_ \| '_ \| | | | '_ \| |
| |\ | (_| | |_) | |_) | |_| | |_) |_|
|_| \_|\__,_| .__/| .__/ \___/| .__/(_)
|_| |_| |_|Napp Up! is a powerful CLI tool for developers to effortlessly upload and manage Nostr applications. Ship your decentralized apps to the Nostr network with a single command.
Usage
nappup [directory] [options]Arguments
[directory]The root directory of your application to upload. If omitted, defaults to the current working directory (.).
Options
| Flag | Description |
|------|-------------|
| -s <secret_key> | Your Nostr secret key (hex, nsec, or bunker:// URL) used to sign the application event. See Authentication for alternatives. |
| -d <d_tag> | The identifier (d tag) for your application. Any UTF-8 text up to 260 characters. If omitted, defaults to the directory name. Avoid generic names like dist or build - use something unique among your other apps like mycoolapp. |
| -y | Skip confirmation prompt. Useful for CI/CD pipelines or automated scripts. |
| -r | Force re-upload. By default, Napp Up! might skip files that haven't changed. Use this flag to ensure everything is pushed fresh; it does not create a new app version unless a file path or hash changes. |
| --main | Publish to the main release channel. This is the default behavior. |
| --next | Publish to the next release channel. Ideal for beta testing or staging builds. |
| --draft | Publish to the draft release channel. Use this for internal testing or work-in-progress builds. |
| --dotenv-private-key <hex> | Use a 32-byte hex private key to decrypt and manage encrypted credentials in .env. Prefer DOTENV_PRIVATE_KEY_NAPPUP because command-line arguments can be visible in shell history and process listings. |
Authentication
Napp Up! supports multiple ways to provide your Nostr secret key:
CLI flag: Pass your secret key (hex or nsec) directly via
-s:nappup -s nsec1...Remote signer (NIP-46): Pass a
bunker://URL to sign events via a remote signer like nak or Amber:nappup -s 'bunker://<pubkey>?relay=wss://relay.example.com&secret=<token>'Napp Up! creates a persistent NIP-46 client key before connecting. For URLs passed with
-s, the latest session is stored asLAST_CLI_BUNKER_SESSIONin the project's.env. A URL withoutsecretis also valid when the remote signer uses its public key as the access capability.Environment variable: Set
NOSTR_SECRET_KEYin your environment or a.envfile (also supportsbunker://URLs):export NOSTR_SECRET_KEY=nsec1... nappup ./distAuto-generated key: If no key is provided, Napp Up! will generate a new keypair automatically and store it (as nsec) in your project's
.envfile for future use.
When NOSTR_SECRET_KEY in .env is a bunker URL, Napp Up! adds the local client key as a #client_key=... URI fragment while retaining the query-string secret for compatibility fallback. Reconnects first use the same client key without the token and retry with the token only if necessary.
Napp Up! stores NOSTR_SECRET_KEY and LAST_CLI_BUNKER_SESSION in the official dotenvx encrypted:<base64> format, using the compressed public key in DOTENV_PUBLIC_KEY_NAPPUP. The private key is selected from --dotenv-private-key, then DOTENV_PRIVATE_KEY_NAPPUP in the process environment, and finally a built-in fallback. The public key is selected from the .env, then the process environment, and finally derived from the selected private key.
The built-in fallback makes migration automatic, but is public knowledge and therefore only hides plaintext; use an explicit private key for confidentiality. Generate one locally for storage in your secret manager with:
nappup env keygenAlternatively, generate and export one directly into the current shell:
export DOTENV_PRIVATE_KEY_NAPPUP="$(nappup env keygen)"env keygen writes only the generated 64-character lowercase hex key to standard output, writes a reminder to standard error, and never reads or modifies .env. Store the result in a secret manager; nappup cannot recover it.
Encrypting a replacement requires only DOTENV_PUBLIC_KEY_NAPPUP. The command
below receives the new Nostr credential (a secret key or bunker URL), but does
not require DOTENV_PRIVATE_KEY_NAPPUP, the separate dotenv decryption key.
It does not need to decrypt or know the previous Nostr credential:
nappup env set NOSTR_SECRET_KEY
printf '%s\n' 'nsec1...' | nappup env set NOSTR_SECRET_KEYThe interactive form hides and confirms the value. Redirected stdin and a positional value are also accepted; the positional form emits a warning because it may be exposed through shell history or process listings. With only DOTENV_PUBLIC_KEY_NAPPUP, env set can encrypt a replacement, while commands that need an existing value fail clearly without the matching private key.
Existing plaintext values are encrypted automatically when used. If an explicit private key does not match the stored public key, it becomes authoritative: recoverable values are re-encrypted and inaccessible credentials are reset with a warning naming only the affected variables. This can result in a new publisher identity or bunker client key.
Use DOTENV_CONFIG_PATH to select a different dotenv file. The private key must come from the process environment or CLI and is rejected if stored inside that file.
Local development and shared credentials
To use a checkout instead of the registry package:
cd /path/to/nappup
npm link
cd /path/to/app
npm link nappup --no-save --package-lock=falseThe second command links the app dependency and its local CLI to the global link.
Projects that use nappup at runtime can keep a registry dependency for reproducible
installs; local CLI-only projects may rely entirely on the link. npm ci removes
local links, so repeat the second command afterward. When switching Node/npm
installations, repeat both commands to register the checkout under the new global
prefix as well.
A link shares code, not credentials: .env still defaults to the current working
directory. To use one existing encrypted identity across projects, export an
absolute path to its dotenv file in the shells running nappup:
export DOTENV_CONFIG_PATH="/absolute/path/to/shared/nappup.env"
nappup env set NOSTR_SECRET_KEYChoose an existing file to retain its identity, or set the desired credential once
in a new file. Supply the matching DOTENV_PRIVATE_KEY_NAPPUP if that file uses a
custom encryption key. Changing credentials in a shared file affects every project
using it; existing project .env files are not automatically merged or migrated.
A process-level NOSTR_SECRET_KEY still takes precedence over the file.
Examples
Upload the current directory to the main channel:
nappup -s nsec1...Or using an environment variable:
NOSTR_SECRET_KEY=nsec1... nappupUpload a specific dist folder with a custom identifier to the next channel:
nappup ./dist -s nsec1... -d "My App #1" --nextForce re-upload a draft:
nappup ~/my-repos/projectx/build/projectx --draft -rProgrammatic Usage
Each published manifest includes an aggregate x tag that identifies the app
version from its file path/hash mappings. Updating only manifest metadata or
forcing a re-upload preserves that aggregate. The published_at tag records
the first publication time of that aggregate and is preserved across later
metadata revisions, while the event's created_at records the latest manifest
revision.
Napp Up! also exports a function that works in both Node.js and the browser, so you can integrate app uploads directly into your own tooling:
import publishApp from 'nappup'
await publishApp(fileList, signer, {
dTag: 'my-app',
channel: 'main', // 'main' | 'next' | 'draft'
shouldReupload: false,
onEvent ({ type, progress }) {
console.log(`${type} — ${progress}%`)
}
})fileList— aFileListor array ofFileobjects (each needswebkitRelativePath).signer— a NIP-07-compatible signer. In the browser,window.nostris used automatically if omitted.onEvent— optional callback that receives progress events with atype('services-checking','init','file-uploaded','complete','error', …) andprogress(0–100).
Rejected uploads use NappupError, with a stable code from
NAPPUP_ERROR_CODES. The original error is retained as cause, and some
errors include structured details, so applications can show their own
recovery instructions without matching CLI-oriented message text.
For terminal destination failures, error.details.failures contains
{ destination, filename?, reason } entries. reason retains the original error,
including HTTP status, retryable, retryAfterMs, or relay category when
available. Native causes and aggregated errors remain available for diagnostics.
Signer failures are normalized to NAPPUP_SIGNER_LOCKED or NAPPUP_SIGNER_DENIED,
including when nested inside an aggregate.
A file succeeds when at least one destination confirms a copy (each chunk for IRFS). Failures of extra replicas are logged without emitting a terminal error. Publishing the app manifest still requires a confirmed copy. Display recovery instructions for rejected operations; use specific destination guidance only when it applies to all blocking failures. Different files may succeed on different servers.
import publishApp, { NAPPUP_ERROR_CODES } from 'nappup'
try {
await publishApp(fileList, signer)
} catch (error) {
if (error.code === NAPPUP_ERROR_CODES.GENERIC_FOLDER_NAME) {
// Ask the user to choose a unique app folder name.
}
}