chore: install ngit repository skill

Add repository guidance for supported coding agents.
This commit is contained in:
greenart7c3
2026-09-09 09:27:09 -03:00
parent 4c1930323c
commit d2ba0b8836
20 changed files with 1401 additions and 1 deletions
+117
View File
@@ -0,0 +1,117 @@
---
name: ngit
description: Commands and workflows for NIP-34 git collaboration over Nostr with the ngit CLI and git-remote-nostr. Use in any repository with a nostr:// remote for generic collaboration requests (open an Issue, create or review a Pull Request (PR), comment, merge, clone) and whenever a task involves nostr:// URLs, ngit commands, Grasp servers, gitworkshop.dev, Nostr CI status and workflows, software releases and Zapstore publication, OCI container images, or nsite static sites published through Blossom.
license: CC-BY-SA-4.0
metadata:
version: "1.17"
---
# ngit — Nostr Plugin for Git
ngit makes `git clone`, `fetch`, and `push` work with `nostr://` URLs and adds
a CLI for pull requests, issues, membership, CI results, releases, OCI
containers, and nsites. Repository state (which commit each ref points to) is
published as signed Nostr events and is the source of truth; git objects live
on ordinary git servers, so servers are interchangeable. A grasp server bundles
a relay and a git server and creates the repository automatically when an
announcement lists it. Explanation: https://ngit.dev/how-it-works
## Where to look
- **This skill documents ngit v3.** Run `ngit --version` first; the commands
here need 3.0.0 or later. If ngit is missing or older, report that and
offer an install or update: `curl -fsSL https://ngit.dev/install.sh | bash`
installs or replaces `ngit` and `git-remote-nostr`, and `ngit update` works
on v3 or later. Other methods: https://ngit.dev/install
- **`ngit <command> --help`** is the authority for the installed version's
flags and defaults.
- **https://ngit.dev** holds the guides. Any page is available as raw
Markdown at `https://ngit.dev/markdown/<route>.md`; the index is
https://ngit.dev/llms.txt. Web UI: https://gitworkshop.dev
## Rules
- **Preserve user and repository choices.** Examples here show syntax, not
policy. Carry through the selected signer, target, hosting, CI, and
replication settings; do not add gates, waits, workflow edits, or
configuration changes that the task or repository did not choose.
- **PR branches MUST start with `pr/`** (e.g. `pr/my-feature`). Any other
branch name is a plain push and never creates a PR.
- **Read ngit output with `--json`.** It is a global option and works at any
position. Stdout is exactly one JSON document; progress and diagnostics go
to stderr. `git` commands have no `--json`. Top-level `command_status` is
`ok` for exit 0 and `error` otherwise; it never describes a nested domain
result (`ngit ci status --json` reports `ok` with
`ci.conclusion: "failure"` unless a gate was requested).
- **Add `--offline` after the first network read** in a session, on commands
that support it. `git fetch origin` also refreshes the cache.
- **Identifiers.** `<ID|nevent>` accepts `nevent1…`, a 64-char hex ID, or a
unique hex prefix with an optional `#` (quote it: `'#deadbeef'`). JSON `id`
and `reply_to` fields are already `nevent1…`; container publication instead
returns a raw-hex `event_id` plus the repository `naddr`. Reference events
inside `--body` text as `nostr:nevent1…` or `nostr:naddr1…`, never as raw
hex. Never construct a NIP-05 address (`user@domain`); use `npub1…` unless
a NIP-05 address was given to you.
- **Multiline text.** `ngit` options such as `--body` and `--description`
accept real newlines: `--body "$(cat note.md)"`. Git push options cannot
carry newlines: write literal `\n` in a short inline `-o description=…`, and
never convert a file into a push option (open the PR with `ngit send`
instead).
- **Signers.** `--signer <alias|npub|profile-name>` selects a stored identity
for one `ngit` command; `git -c nostr.signer=<alias|npub|profile-name>
push …` does the same for one git command. Neither changes the configured
login. Never export or pass an nsec merely to switch between configured
accounts.
- **CI.** A successful push says nothing about CI. Nostr CI runs workflows
from `.ngit/act/workflows/`; another provider's directory is not evidence.
When CI matters, query the exact commit or PR with
`ngit ci status <target> --json` and read `ci.state` and `ci.conclusion`,
not `command_status`.
- **Target repository.** With several `nostr://` remotes, pass global
`--repo <REMOTE|NADDR|NOSTR-URL>`; a configured remote name, an naddr, and
a nostr:// URL are all accepted. Without it ngit
infers the target from config and branch tracking and fails rather than
guesses. Before a signing command, check the
`target repository: <naddr> (source: …)` line on stderr.
## Detecting a nostr repo
```bash
git remote -v | grep -q 'nostr://' # primary check, no cache needed
ngit repo --json --offline # full metadata when needed
```
`ngit repo` always exits 0, and `is_nostr_repo: false` can be a cold-cache
false negative: if a remote shows `nostr://`, run `git fetch origin` and
retry. The output includes the roster (`members`, `lead_source`, `lead_path`,
`pending_actions`, `health`); read `reference/repositories.md` before
changing membership or hosting.
## nostr:// URLs
```
nostr://<npub>/<identifier>
nostr://<npub>/<relay-hint>/<identifier> # relay-hint is a bare domain, e.g. relay.ngit.dev
nostr://<user>@<domain>/<identifier> # NIP-05, only when explicitly provided
nostr://<domain>/<repository-path> # NIP-AD: the full /path is sent URL-encoded to /.well-known/nostr.json?path=
```
Standard git commands accept these URLs directly.
## Task index
Read the bundled reference before performing that slice of work; the guide
adds tutorials and background.
| Task | Bundled reference | ngit.dev guide |
| ---- | ----------------- | -------------- |
| Publish, clone, host repositories; maintainers, moderators | `reference/repositories.md` | `/repositories`, `/maintainers` |
| Open, update, stack, review, merge PRs | `reference/prs.md` | `/pull-requests` |
| Issues | `reference/issues.md` | `/issues` |
| CI results, trust, workflows, ngit in CI jobs | `reference/ci.md` | `/ci`, `/ci/workflows/` |
| Accounts, login, signers, secrets | `reference/accounts.md` | `/accounts` |
| Sync, global flags, git config | `reference/sync-config.md` | `/configuration`, `/troubleshooting` |
| Publish nsites (static sites) | `reference/nsites.md` | `/releases/nsites` |
| Publish OCI containers | `reference/containers.md` | `/releases` |
| Software releases | `ngit release --help` | `/releases` |
| Automation contract, machine-readable docs | this file | `/agents/` |
+47
View File
@@ -0,0 +1,47 @@
# Accounts — identity, login, secrets
Read when managing accounts, logins, or credential storage.
Guide: https://ngit.dev/accounts (storage modes, pairing a remote signer for
CI, rotation).
```bash
ngit account whoami --json --offline # every usable signer with npub, aliases, scope, active state; `account list` is an alias
ngit account login # interactive; the secret goes to the OS credential store or ngit's user-only file store
ngit account login alice # make a stored identity the global default (alias, npub, or exact profile name)
ngit account login --local alice # …this repository's default, including for git push
ngit account login --nsec-file /private/key --alias alice
ngit account login --nbunksec-file /private/connection --alias alice # store an established NIP-46 session
ngit account login --bunker-url bunker://... # pair a remote signer
ngit account login --local -i --alias alice # pair a fresh signer and assign or replace this alias
ngit account login --secret-storage file # bypass the OS store; `git-config` stores plaintext and must be explicit
ngit account create --name "Alice" --json
ngit account export-keys --secret # print only the nsec or nbunksec; --json adds the npub
ngit account logout --json # keeps stored keys; add --forget to delete the secret
ngit account forget-keys <entry> --json
ngit --signer alice issue create --subject "Bug" --body "Details" --json # one ngit command as alice
git -c nostr.signer=alice push origin pr/topic # one git command as alice
ngit --nsec-file /private/key <command> # one-shot key for CI; --nbunksec-file for a bunker session
```
**Storage.** `auto` (OS store, then the file store), `file`, or `git-config`,
selected with `--secret-storage`, `NGIT_SECRET_STORAGE`, or
`nostr.secret-storage`. Git config holds the credential entry name, not the
secret. Existing plaintext values keep working.
**Selection.** `--signer` and `nostr.signer` accept an alias, an npub, or an
exact cached profile name. A profile name must match exactly one account that
holds stored credentials, and only the resolved npub is persisted. Selection
fails closed when the selector is missing, ambiguous, or backed by invalid
credentials.
**Several NIP-46 connections for one npub.** The first connection stays the
default selected by the bare npub; ngit refuses to replace it with another
unaliased connection. Log in with `--alias` to keep an extra connection and
select it by alias. Older ngit versions ignore the exact-session binding and
select the identity's default connection.
**nbunksec** is a portable established connection: remote-signer pubkey,
client secret, relays, and optional pairing secret. It holds no npub, so
one-shot use resolves the identity from the signer. The `--nbunksec-file` and
`--nsec-file` forms keep secrets out of process arguments. A fresh pairing
needs interactive approval, so unattended runs use a stored connection.
+67
View File
@@ -0,0 +1,67 @@
# Nostr CI
Read when checking whether CI ran, interpreting a result, or writing a
workflow that uses ngit. Guides: https://ngit.dev/ci (coordinators, results,
trust, secrets) and https://ngit.dev/ci/workflows/ (what the coordinator
accepts, refuses, and adds compared with GitHub Actions).
## Workflows
Nostr CI (ngit-ci) runs workflows from `.ngit/act/workflows/` with
GitHub Actions syntax in Linux containers. `.github/workflows/` is run only by
GitHub Actions on a mirror; the directories are independent, so a check that
must run in both systems needs a file in each. ngit-ci refuses macOS and
Windows `runs-on` labels and job-level `uses:` (reusable workflows); composite
actions in steps work in both systems.
Read the workflow at the commit under investigation and confirm that its
triggers and steps cover the check in question:
```bash
git show <COMMIT>:.ngit/act/workflows/<WORKFLOW>.yaml
```
Install `ngit` and `git-remote-nostr` inside an ngit-ci or GitHub Actions job
with the step-level composite action, which verifies downloads against a
checksum-pinned manifest:
```yaml
- uses: danconwaydev/setup-ngit@v3
with:
version: 3.0.0 # optional exact pin; the default `latest` resolves against the action's manifest, not the network
```
Source:
`nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/relay.ngit.dev/setup-ngit`
(GitHub mirror `DanConwayDev/setup-ngit`).
## Query a result
```bash
ngit ci status <COMMIT|'#prefix'|nevent> --json # first query refreshes relays; no target means HEAD
ngit ci status <target> --json --offline # later cache-only reads
ngit ci status <target> --require-ci-trust maintainer-directed --json # exit non-zero unless green at this floor
```
Query the exact commit that introduced the change. A PR target reports only
its latest revision. Read:
- `ci.state`: pending, running, or concluded. `ci.conclusion` counts only
once the state is concluded.
- `ci.conclusion`: success, failure, cancellation, or another outcome.
`command_status: "ok"` means only that the query worked.
- `ci.runs[].workflow` and `ci.runs[].jobs`: which workflow and job passed or
failed.
- `ci.runs[].integrity`: the commit is present locally and the workflow hash
matches.
- `coverage` and each run's classification and evidence: how completely and
why the result is trusted. Partial coverage is not success.
If no run appears, report that no matching Nostr CI event was found. Then
check that the workflow existed at that commit, its trigger matched, a
coordinator serves the repository, and the query refreshed the relays before
concluding that CI did not run.
Trust floors are `maintainer-directed` and `operationally-associated`.
`ngit pr merge --require-ci-trust <LEVEL>` applies the same gate to a merge
when the caller wants one.
+103
View File
@@ -0,0 +1,103 @@
# Containers — publish OCI images
Read before publishing an OCI image, updating a container tag, choosing
Blossom storage, or constructing a gateway pull reference. Protocol
background: https://ngit.dev/protocol/software-publishing
## Model
`ngit container publish` (alias `ngit oci publish`) uploads the OCI blobs
reachable from the tagged entries of an OCI image layout to Blossom, then
signs a kind-30624 addressable event mapping tags to manifest digests. The
event is bound to the current kind-30617 git repository, so run it inside that
repository with a signer who is a confirmed maintainer. Gateways are read-only:
```bash
docker pull ncontainer.io/<npub>/<repository>:<tag>
```
## Publish
Export an OCI image layout first, for example
`podman push myimage oci:/tmp/myimage:latest`, then publish every tagged root:
```bash
ngit container publish myimage \
--layout /tmp/myimage \
--blossom-server https://blossom-one.example \
--blossom-server https://blossom-two.example \
--relay wss://relay.example \
--source https://example.com/myimage \
--json
```
A checked-in `.ngit/containers.yaml` lets `ngit container publish myimage
--json` select an entry:
```yaml
schema: 1
publication:
blossom_servers: [https://blossom-one.example, https://blossom-two.example]
relays: [wss://relay.example]
containers:
myimage:
layout: artifacts/myimage
source: https://example.com/myimage
```
Relative paths resolve from the repository root. `--manifest PATH` selects
another file; `--no-manifest` ignores the default and requires `--layout`. A
loaded manifest must define `NAME`. CLI layout and metadata override the
entry, a non-empty CLI Blossom list replaces the configured list, and CLI
relays extend configured relays. Signer selection, `--replace`, and output
mode stay on the command line.
Behaviour to know:
- `NAME` is one lowercase OCI repository-name component.
- Tags come from `org.opencontainers.image.ref.name` annotations in
`index.json`; filenames and git tags are irrelevant. `index.json` itself is
never uploaded; ngit merges the layout's tags into the tag map fetched from
the latest kind-30624 event.
- Without `--blossom-server`, ngit uses the publisher's latest kind-10063
server list. A single server means no redundancy. Every blob is checked on
every server, missing copies are uploaded with bounded retries and
verified, and the event is signed once each blob has at least one confirmed
copy; incomplete replication is reported per server.
- `--relay` extends the repository's relays; account and default relays are
not added. ngit reads the repository relays before and after uploading and
needs at least one success each time. A total preflight failure or a
concurrent-update refusal is safe to retry: uploaded blobs are
content-addressed. Keep a known state-bearing repository relay reachable
when changing relay sets, because a healthy empty relay cannot reveal an
event stranded elsewhere and a publish could then omit old tags.
## Merge versus replace
Ordinary publication updates the tags found in the new layout and retains
older tags, previous server hints, omitted metadata, and unknown event tags.
`--replace` publishes only the new layout's tags and selected servers, drops
omitted description, source, and unknown tags, and sets the title to
`--title` or `NAME`. Use it only when the user explicitly wants complete
replacement.
## JSON
A successful result has `command: "container.publish"`, a `warnings` array,
and `result` fields: `repository`, `git_repository`, `npub`, `name`, `naddr`,
`manifest_path` (or `null`), raw-hex `event_id` (unlike collaboration
commands' `nevent` ids), `tags` and `updated_tags`, per-blob SHA-256, size,
and per-server placement, final `blossom_servers`, and per-relay `accepted`.
Success means at least one relay accepted the event; inspect every
`result.relays[].accepted` when full fanout matters. Failures use
`command_status: "error"` with `error.details` holding per-blob and
per-server outcomes and possible orphan blobs.
## Limits
ngit accepts OCI and Docker v2 manifests and indexes using SHA-256, uploads
only blobs reachable from tagged roots, and rejects missing, oversized, deeply
nested, or mismatched graphs. It snapshots one blob at a time, so allow
temporary disk roughly equal to the largest layer. It does not build images,
push to registries, run a gateway, chunk layers, pull, list, or delete remote
blobs or tags. Software release assets use the separate `ngit release` model.
+29
View File
@@ -0,0 +1,29 @@
# Issues — create, view, comment, close
Read when working with issues. Guide: https://ngit.dev/issues
```bash
ngit issue create --subject "Bug title" --body "Details as markdown" --label bug --json
ngit issue list --json # add --status closed or --label bug to filter
ngit issue view <ID|nevent> --json --comments
ngit issue view <ID|nevent> --json --history # subject, cover-note, label, and status changes
ngit issue comment <ID|nevent> --body "Reproduced on v2.1" --json
ngit issue comment <ID|nevent> --body "Thanks!" --reply-to <comment-ID|nevent> --json
ngit issue close <ID|nevent> --reason "wontfix" --json
ngit issue resolved <ID|nevent> --reason "fixed in abc123" --json
ngit issue reopen <ID|nevent> --reason "regression in v2.3" --json
ngit issue label <ID|nevent> --label bug --label enhancement --json
ngit issue set-subject <ID|nevent> --subject "New title" --json
ngit issue set-cover-note <ID|nevent> --body "$(cat cover-note.md)" --json
```
`resolved` records that the problem was fixed; `close` records that it will
not be. Reference other events in `--body` as `nostr:nevent1…`.
## Auto-resolve from commits
A commit pushed to the declared default branch resolves an issue when its
message contains a form of `close`, `fix`, `resolve`, or `implement` followed
by a unique hex ID or prefix or a `nostr:nevent1…` reference, for example
`Fixes #deadbeef`. The status is published only when the pusher is the issue
author or a confirmed repository member.
+71
View File
@@ -0,0 +1,71 @@
# Nsites — publish static sites
Read before publishing an already-built website with `ngit nsite` or
diagnosing its Blossom uploads and NIP-5A manifest.
Guide: https://ngit.dev/releases/nsites (nsyte comparison, PR previews).
## Publish
Pass the build output directory, not the source tree. ngit uploads every
regular file, runs no build, applies no ignore files, and rejects symlinks,
unsafe paths, and filenames without extensions.
```bash
ngit nsite publish dist --json # reads nsyte's .nsite/config.json when present
ngit nsite publish dist --title "My site" --json # root kind-15128 site
ngit nsite publish dist --id docs \
--description-file site-description.txt \
--source "nostr://<npub>/<identifier>" \
--blossom-server https://blossom.example.com \
--blossom-server https://mirror.example.com \
--relay wss://relay.example.com --json # named kind-35128 site
ngit --signer <alias> nsite publish dist --json
ngit --nbunksec-file /run/secrets/publisher-nbunksec nsite publish dist --json # unattended NIP-46
```
- Config: `.nsite/config.json` (JSON, not YAML) fields `id`, `title`,
`description`, `source`, `fallback`, `servers`, and `relays` are read;
`--config PATH` selects another file and `--no-config` ignores it. Explicit
CLI values win, and any repeated `--blossom-server` or `--relay` replaces
that whole config array. Unsupported nsyte publication options
(`publishProfile`, `publishRelayList`, `publishServerList`,
`publishAppHandler`) produce a warning; nsyte signer fields are ignored.
- Metadata: `--title`, `--description` or `--description-file`, and
`--source` (`https://` or `nostr://`; omitted, ngit infers the selected
public repository and never a private one). NIP-5A has no logo tag; ship a
`favicon.ico` or `favicon.svg` in the build output.
- `--fallback SITE_PATH` (or config `fallback`) maps an existing HTML file in
the output to `/404.html` without another upload.
- Servers: omit `--blossom-server` to use the account's latest kind-10063
list; repeat it for replication. `--concurrency` (default 4, range 164) is
a global limit across presence checks and uploads.
## Guarantees
ngit snapshots the directory before network work, deduplicates content, checks
every blob on every selected server, and uploads missing copies with BUD-11
authorization. It signs the manifest only after every blob has at least one
confirmed copy, so a failed deployment cannot point the live manifest at
missing content. A server that fails three consecutive initial checks is
skipped for the rest of that pass while the others continue.
Rerun the same command after a failure: blobs already on a server are
confirmed with `HEAD` and skipped, so continuation is per blob and server. An
unchanged deployment reuses the current manifest without a new signature or
relay write.
## JSON
Check `command_status`, then:
- `result.changed`: publication versus an unchanged no-op;
- `result.config_path`, `result.fallback`, `result.relays`: resolved settings;
- `result.blossom.blobs[].servers[]`: each blob and server outcome;
- `result.publication.relays[]`: manifest acknowledgements (at least one
relay must accept);
- `warnings[]`: unknown MIME types, unsupported config publications, and
failed uploads or post-upload verification per server.
On a Blossom failure inspect `error.details.blobs` and
`error.details.possible_orphan_blobs`, fix the server or signer problem, and
rerun.
+96
View File
@@ -0,0 +1,96 @@
# Pull requests — open, update, stack, review, merge
Read before opening, updating, reviewing, or merging PRs.
Guide: https://ngit.dev/pull-requests
## Open or update a PR
The branch name MUST start with `pr/`. No push option turns another branch
into a PR.
```bash
git checkout -b pr/my-feature
git push -u origin pr/my-feature # one commit: its subject and body become title and description
git push -u origin pr/my-feature \
-o 'title=My feature' \
-o 'description=First paragraph.\n\nSecond paragraph.' # literal \n; push options cannot carry real newlines
git push -u origin pr/release-fix -o target-branch=release/2.x
git push -u origin pr/second-part -o base=<commit|branch|nevent> # pin or override the stack parent
git push --force origin pr/my-feature # update the PR after amending or rebasing
```
- `-d`/`--defaults` accepts the single-commit title and description without a
prompt.
- Do not use `$'…\n…'` for push options, and do not pre-escape a Markdown
file into `-o description=`; open the PR with `ngit send` instead.
- Stacks are inferred: a branch that contains the unique latest tip of one of
your other open or draft PRs becomes that PR's child and follows the parent
as it advances. Rebase the child onto the parent's latest tip before updating
it; ngit refuses stale children and ambiguous candidates rather than
guessing. Use `base=` for a cross-author, historical, or ambiguous parent,
and repeat it on each update if the child should stay pinned.
- To push as another stored identity, use
`git -c nostr.signer=<alias|npub|profile-name> push …`; `--signer` applies
to `ngit` commands only. `ngit account login --local <alias>` makes an
identity the repository default instead.
## ngit send
`ngit send` takes ordinary shell arguments, so `--description` accepts real
newlines from `$'…'` or `"$(cat file.md)"`. Inside double quotes `\n` stays a
literal backslash-n.
```bash
ngit send HEAD~2 --subject "My feature" --description "$(cat pr-description.md)" --json
ngit send HEAD~2 --in-reply-to <PR-ID|nevent> --json # new revision of an existing PR
ngit send --defaults --target-branch release/2.x --json
ngit send --defaults --base <commit|branch|nevent> --json
```
Do not also push a `pr/` branch for the same proposal.
## Read, comment, check out
```bash
ngit pr list --json # default filter: open,draft
ngit pr list --json --status open,draft,closed,applied --label bug
ngit pr view <ID|nevent> --json --comments
ngit pr comment <ID|nevent> --body "Looks good" --json
ngit pr comment <ID|nevent> --body "Fixed!" --reply-to <comment-ID|nevent> --json
ngit pr checkout <ID|nevent> --json # local tracking branch that git pull/push understand
```
## Merge (maintainer)
```bash
ngit pr merge <ID|nevent> --json # no-ff merge commit on the PR's target; does not push
ngit pr merge --json # PR inferred from the checked-out pr/ branch
ngit pr merge <ID|nevent> --require-ci-trust maintainer-directed --json # refuse unless CI is green at this trust floor
ngit pr merge <ID|nevent> --exclude-description --json # summary line and PR reference only
git push origin <target-branch> # publishes the merge and the applied status
```
`ngit merge` is a compatibility alias with the same options. The merge lands
on the PR's declared target, or the default branch, resolved against the
latest Nostr repository state rather than a local tracking ref, with the
message `Merge #<8-hex>: <PR title>`. Closed and applied PRs are refused
before any git change. On conflicts, resolve them and run `git commit`; the
message is already prepared, and JSON reports `action: "conflicted"` instead
of `"merged"`.
Before merging or adding maintainer fixes, run
`git log --merges --oneline origin/<target>..HEAD`. A prior `Merge #…` means a
merge would nest merge history; rebase or cherry-pick the PR commits onto the
current target first unless that history is intentional.
## Lifecycle
```bash
ngit pr close <ID|nevent> --reason "blocked by upstream" --json
ngit pr reopen <ID|nevent> --reason "fix was incomplete" --json
ngit pr ready <ID|nevent> --reason "addressed review feedback" --json
ngit pr draft <ID|nevent> --reason "needs more work" --json
ngit pr label <ID|nevent> --label bug --label enhancement --json
ngit pr set-subject <ID|nevent> --subject "New title" --json
ngit pr set-cover-note <ID|nevent> --body "Updated description. See nostr:nevent1abc…" --json
```
@@ -0,0 +1,119 @@
# Repositories — publish, clone, hosting, membership
Read when publishing or cloning a repository, resolving `nostr://` URL forms,
or changing an announcement's hosting, metadata, or roster. Guides:
https://ngit.dev/repositories (hosting choices, migrating from a forge,
mirrors, private repositories), https://ngit.dev/maintainers, and
https://ngit.dev/maintainers/going-deeper (leadless repositories, delegated
trust, removal, roster repair).
## URLs
```
nostr://<npub>/<identifier>
nostr://<npub>/<relay-hint>/<identifier> # relay-hint is a bare domain, e.g. relay.ngit.dev
nostr://<user>@<domain>/<identifier> # NIP-05, only when explicitly provided
nostr://<domain>/<repository-path> # NIP-AD: the full /path is sent URL-encoded to /.well-known/nostr.json?path=
```
## Clone
```bash
git clone nostr://<npub>/<relay-hint>/<identifier> # relay hint skips discovery
git clone nostr://<npub>/<identifier>
git clone nostr://user@domain.com/<identifier> # NIP-05, only if given to you
git clone nostr://ngit.dev/ngit.git # NIP-AD bare-domain path
```
Open and draft PRs are not fetched as branches unless `nostr.auto-pr-branches`
is `true`; `ngit pr checkout <ID|nevent>` materialises one on demand.
## Inspect
```bash
ngit repo --json --offline # run git fetch origin first when the cache may be stale
```
The output reports `nostr_url`, effective `git_servers`, `relays`, `hashtags`,
and `grasp_servers` detected from paired clone and relay entries, plus the
roster: `members`, `lead_source`, `lead_path`, `pending_actions`, and
`health`. Follow the actionable error or `pending_actions` rather than
replacing an announcement wholesale.
## Publish and host
`ngit init` declares the complete initial announcement. Grasp hosting supplies
both a git server and a relay; additional infrastructure is explicit, empty by
default, and supplements grasp hosting rather than replacing it:
```
relays = grasp-derived relays + additional relays
clones = grasp-derived clones + additional clones
```
```bash
ngit init --name "My Project" --description "What it does" --defaults --json # preferred grasp servers, else ngit defaults
ngit init --name "My Project" --grasp-server grasp.example.com --defaults --json
ngit init --name "My Project" --additional-relay wss://relay.example.com \
--additional-clone https://git.example.com/my-project.git --defaults --json
ngit init --name "My Project" --grasp-server "" \
--additional-relay wss://relay.example.com \
--additional-clone https://git.example.com/my-project.git --defaults --json # no grasp: both halves required
```
Every announcement needs at least one relay and one git server; `init` and
`repo edit` refuse to publish otherwise, including a metadata-only edit of an
announcement that already lacks one half. Repair it in the same command, e.g.
`ngit repo edit --name "New name" --add-grasp-server grasp.example.com`.
`--identifier` is set at initial publication only: changing the `d` tag
creates a different repository coordinate.
## Edit
`ngit repo edit` preserves omitted settings. Collections use targeted,
repeatable actions that can be combined in one command:
| Setting | Add | Remove |
| ------- | --- | ------ |
| Grasp server | `--add-grasp-server URL` | `--remove-grasp-server URL` |
| Additional relay | `--add-additional-relay URL` | `--remove-additional-relay URL` |
| Additional clone | `--add-additional-clone URL` | `--remove-additional-clone URL` |
| Hashtag | `--add-hashtag TAG` | `--remove-hashtag TAG` |
Scalars use replacement flags: `--name`, `--description`, `--web`, `--u`,
`--earliest-unique-commit`. A grasp-derived relay or clone cannot be removed
as an additional entry; remove the grasp server and its pair goes with it. To
empty a collection, remove every value currently reported.
Each successful edit publishes a fresh announcement and, when the repository
has Nostr state, republishes that state once so new relays and servers hold
the authoritative refs. If that fails, follow the reported `ngit sync`
recovery guidance.
## Roles and membership
- **Co-maintainer**: publishes git state, merges, manages issues and PRs, and
changes the roster at the protocol level.
- **Lead maintainer**: the same authority plus responsibility for the roster.
When a lead exists, ngit restricts routine roster changes to the lead
workflow.
- **Moderator**: publishes issue, PR, and patch status events (including
recording an existing merge) but cannot publish git state or merge.
Membership is reciprocal: a listing is an invitation until the invitee
publishes an announcement acknowledging the role, and an invited member's
events are not authoritative until then.
```bash
ngit repo edit --add-maintainer <npub> --json # a sole maintainer's first add makes them lead
ngit repo accept --json # invitee confirms the role and the lead; --grasp-server <url> also hosts the git data there
ngit repo follow-lead --json # members retain history and follow a changed lead or roster
ngit repo leave --json # end your own role and republish
ngit repo edit --remove-maintainer <npub> --json
ngit repo edit --lead-maintainer <npub> --json # handover: the new lead publishes the full roster first
ngit repo edit --acknowledge-maintainer-change <npub> --json
```
Change one relationship at a time. In the deliberately leadless case, pass
`--no-lead-maintainer` with every `--add-maintainer` or `--remove-maintainer`.
@@ -0,0 +1,49 @@
# Sync, flags, and configuration
Read when syncing refs, choosing flags, or tuning git config.
Guides: https://ngit.dev/configuration and https://ngit.dev/troubleshooting
## Sync
```bash
ngit sync --json # make git servers reflect the Nostr state for every ref
ngit sync --ref-name main --json # one ref
```
## Global flags
These accept any command position. `--offline` is per command; check
`ngit <command> --help`.
| Flag | Description |
| ---- | ----------- |
| `--json` | One JSON document on stdout (ngit commands only) |
| `-d`, `--defaults` | Non-interactive; accept defaults |
| `-q`, `--quiet` | Hide non-essential stderr progress (not combinable with `-v`) |
| `--repo <REMOTE\|NADDR\|NOSTR-URL>` | Select the target repository |
| `--signer <ALIAS\|NPUB\|NAME>` | Use a stored signer for this command |
| `--nsec-file`, `--nbunksec-file <PATH>` | One-shot key or bunker session from a private file (`--nsec`, `--nbunksec` take inline values) |
| `--repo-relay-only` | Publish only to repository relays |
| `-f`, `--force` | Bypass safety guards |
## git config
```bash
ngit --customize # list every option
git config nostr.signer alice # repository signer, including for git push
git config nostr.signer-alias.alice npub1... # portable alias-to-npub mapping
git config nostr.secret-storage file # auto | file | git-config
git config nostr.repo-relay-only true
git config nostr.auto-pr-branches true # fetch every open and draft PR as a pr/* branch (default false)
git config nostr.http-io-timeout-ms 600000 # allow large grasp pushes
NGIT_CACHE_DIR=/writable/path ngit repo --json # override the global event-cache directory
```
`nostr.auto-pr-branches` follows normal git config precedence. With the
default `false`, PRs appear as branches only after `ngit pr checkout`; run
`git fetch --prune` once to drop branches fetched by an older version, or use
`git clone --config nostr.auto-pr-branches=true <nostr-url>` to opt in from
the start.
If the global cache directory is unavailable ngit falls back to an in-memory
cache; the repository's git common directory must still be writable.
+117
View File
@@ -0,0 +1,117 @@
---
name: ngit
description: Commands and workflows for NIP-34 git collaboration over Nostr with the ngit CLI and git-remote-nostr. Use in any repository with a nostr:// remote for generic collaboration requests (open an Issue, create or review a Pull Request (PR), comment, merge, clone) and whenever a task involves nostr:// URLs, ngit commands, Grasp servers, gitworkshop.dev, Nostr CI status and workflows, software releases and Zapstore publication, OCI container images, or nsite static sites published through Blossom.
license: CC-BY-SA-4.0
metadata:
version: "1.17"
---
# ngit — Nostr Plugin for Git
ngit makes `git clone`, `fetch`, and `push` work with `nostr://` URLs and adds
a CLI for pull requests, issues, membership, CI results, releases, OCI
containers, and nsites. Repository state (which commit each ref points to) is
published as signed Nostr events and is the source of truth; git objects live
on ordinary git servers, so servers are interchangeable. A grasp server bundles
a relay and a git server and creates the repository automatically when an
announcement lists it. Explanation: https://ngit.dev/how-it-works
## Where to look
- **This skill documents ngit v3.** Run `ngit --version` first; the commands
here need 3.0.0 or later. If ngit is missing or older, report that and
offer an install or update: `curl -fsSL https://ngit.dev/install.sh | bash`
installs or replaces `ngit` and `git-remote-nostr`, and `ngit update` works
on v3 or later. Other methods: https://ngit.dev/install
- **`ngit <command> --help`** is the authority for the installed version's
flags and defaults.
- **https://ngit.dev** holds the guides. Any page is available as raw
Markdown at `https://ngit.dev/markdown/<route>.md`; the index is
https://ngit.dev/llms.txt. Web UI: https://gitworkshop.dev
## Rules
- **Preserve user and repository choices.** Examples here show syntax, not
policy. Carry through the selected signer, target, hosting, CI, and
replication settings; do not add gates, waits, workflow edits, or
configuration changes that the task or repository did not choose.
- **PR branches MUST start with `pr/`** (e.g. `pr/my-feature`). Any other
branch name is a plain push and never creates a PR.
- **Read ngit output with `--json`.** It is a global option and works at any
position. Stdout is exactly one JSON document; progress and diagnostics go
to stderr. `git` commands have no `--json`. Top-level `command_status` is
`ok` for exit 0 and `error` otherwise; it never describes a nested domain
result (`ngit ci status --json` reports `ok` with
`ci.conclusion: "failure"` unless a gate was requested).
- **Add `--offline` after the first network read** in a session, on commands
that support it. `git fetch origin` also refreshes the cache.
- **Identifiers.** `<ID|nevent>` accepts `nevent1…`, a 64-char hex ID, or a
unique hex prefix with an optional `#` (quote it: `'#deadbeef'`). JSON `id`
and `reply_to` fields are already `nevent1…`; container publication instead
returns a raw-hex `event_id` plus the repository `naddr`. Reference events
inside `--body` text as `nostr:nevent1…` or `nostr:naddr1…`, never as raw
hex. Never construct a NIP-05 address (`user@domain`); use `npub1…` unless
a NIP-05 address was given to you.
- **Multiline text.** `ngit` options such as `--body` and `--description`
accept real newlines: `--body "$(cat note.md)"`. Git push options cannot
carry newlines: write literal `\n` in a short inline `-o description=…`, and
never convert a file into a push option (open the PR with `ngit send`
instead).
- **Signers.** `--signer <alias|npub|profile-name>` selects a stored identity
for one `ngit` command; `git -c nostr.signer=<alias|npub|profile-name>
push …` does the same for one git command. Neither changes the configured
login. Never export or pass an nsec merely to switch between configured
accounts.
- **CI.** A successful push says nothing about CI. Nostr CI runs workflows
from `.ngit/act/workflows/`; another provider's directory is not evidence.
When CI matters, query the exact commit or PR with
`ngit ci status <target> --json` and read `ci.state` and `ci.conclusion`,
not `command_status`.
- **Target repository.** With several `nostr://` remotes, pass global
`--repo <REMOTE|NADDR|NOSTR-URL>`; a configured remote name, an naddr, and
a nostr:// URL are all accepted. Without it ngit
infers the target from config and branch tracking and fails rather than
guesses. Before a signing command, check the
`target repository: <naddr> (source: …)` line on stderr.
## Detecting a nostr repo
```bash
git remote -v | grep -q 'nostr://' # primary check, no cache needed
ngit repo --json --offline # full metadata when needed
```
`ngit repo` always exits 0, and `is_nostr_repo: false` can be a cold-cache
false negative: if a remote shows `nostr://`, run `git fetch origin` and
retry. The output includes the roster (`members`, `lead_source`, `lead_path`,
`pending_actions`, `health`); read `reference/repositories.md` before
changing membership or hosting.
## nostr:// URLs
```
nostr://<npub>/<identifier>
nostr://<npub>/<relay-hint>/<identifier> # relay-hint is a bare domain, e.g. relay.ngit.dev
nostr://<user>@<domain>/<identifier> # NIP-05, only when explicitly provided
nostr://<domain>/<repository-path> # NIP-AD: the full /path is sent URL-encoded to /.well-known/nostr.json?path=
```
Standard git commands accept these URLs directly.
## Task index
Read the bundled reference before performing that slice of work; the guide
adds tutorials and background.
| Task | Bundled reference | ngit.dev guide |
| ---- | ----------------- | -------------- |
| Publish, clone, host repositories; maintainers, moderators | `reference/repositories.md` | `/repositories`, `/maintainers` |
| Open, update, stack, review, merge PRs | `reference/prs.md` | `/pull-requests` |
| Issues | `reference/issues.md` | `/issues` |
| CI results, trust, workflows, ngit in CI jobs | `reference/ci.md` | `/ci`, `/ci/workflows/` |
| Accounts, login, signers, secrets | `reference/accounts.md` | `/accounts` |
| Sync, global flags, git config | `reference/sync-config.md` | `/configuration`, `/troubleshooting` |
| Publish nsites (static sites) | `reference/nsites.md` | `/releases/nsites` |
| Publish OCI containers | `reference/containers.md` | `/releases` |
| Software releases | `ngit release --help` | `/releases` |
| Automation contract, machine-readable docs | this file | `/agents/` |
+47
View File
@@ -0,0 +1,47 @@
# Accounts — identity, login, secrets
Read when managing accounts, logins, or credential storage.
Guide: https://ngit.dev/accounts (storage modes, pairing a remote signer for
CI, rotation).
```bash
ngit account whoami --json --offline # every usable signer with npub, aliases, scope, active state; `account list` is an alias
ngit account login # interactive; the secret goes to the OS credential store or ngit's user-only file store
ngit account login alice # make a stored identity the global default (alias, npub, or exact profile name)
ngit account login --local alice # …this repository's default, including for git push
ngit account login --nsec-file /private/key --alias alice
ngit account login --nbunksec-file /private/connection --alias alice # store an established NIP-46 session
ngit account login --bunker-url bunker://... # pair a remote signer
ngit account login --local -i --alias alice # pair a fresh signer and assign or replace this alias
ngit account login --secret-storage file # bypass the OS store; `git-config` stores plaintext and must be explicit
ngit account create --name "Alice" --json
ngit account export-keys --secret # print only the nsec or nbunksec; --json adds the npub
ngit account logout --json # keeps stored keys; add --forget to delete the secret
ngit account forget-keys <entry> --json
ngit --signer alice issue create --subject "Bug" --body "Details" --json # one ngit command as alice
git -c nostr.signer=alice push origin pr/topic # one git command as alice
ngit --nsec-file /private/key <command> # one-shot key for CI; --nbunksec-file for a bunker session
```
**Storage.** `auto` (OS store, then the file store), `file`, or `git-config`,
selected with `--secret-storage`, `NGIT_SECRET_STORAGE`, or
`nostr.secret-storage`. Git config holds the credential entry name, not the
secret. Existing plaintext values keep working.
**Selection.** `--signer` and `nostr.signer` accept an alias, an npub, or an
exact cached profile name. A profile name must match exactly one account that
holds stored credentials, and only the resolved npub is persisted. Selection
fails closed when the selector is missing, ambiguous, or backed by invalid
credentials.
**Several NIP-46 connections for one npub.** The first connection stays the
default selected by the bare npub; ngit refuses to replace it with another
unaliased connection. Log in with `--alias` to keep an extra connection and
select it by alias. Older ngit versions ignore the exact-session binding and
select the identity's default connection.
**nbunksec** is a portable established connection: remote-signer pubkey,
client secret, relays, and optional pairing secret. It holds no npub, so
one-shot use resolves the identity from the signer. The `--nbunksec-file` and
`--nsec-file` forms keep secrets out of process arguments. A fresh pairing
needs interactive approval, so unattended runs use a stored connection.
+67
View File
@@ -0,0 +1,67 @@
# Nostr CI
Read when checking whether CI ran, interpreting a result, or writing a
workflow that uses ngit. Guides: https://ngit.dev/ci (coordinators, results,
trust, secrets) and https://ngit.dev/ci/workflows/ (what the coordinator
accepts, refuses, and adds compared with GitHub Actions).
## Workflows
Nostr CI (ngit-ci) runs workflows from `.ngit/act/workflows/` with
GitHub Actions syntax in Linux containers. `.github/workflows/` is run only by
GitHub Actions on a mirror; the directories are independent, so a check that
must run in both systems needs a file in each. ngit-ci refuses macOS and
Windows `runs-on` labels and job-level `uses:` (reusable workflows); composite
actions in steps work in both systems.
Read the workflow at the commit under investigation and confirm that its
triggers and steps cover the check in question:
```bash
git show <COMMIT>:.ngit/act/workflows/<WORKFLOW>.yaml
```
Install `ngit` and `git-remote-nostr` inside an ngit-ci or GitHub Actions job
with the step-level composite action, which verifies downloads against a
checksum-pinned manifest:
```yaml
- uses: danconwaydev/setup-ngit@v3
with:
version: 3.0.0 # optional exact pin; the default `latest` resolves against the action's manifest, not the network
```
Source:
`nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/relay.ngit.dev/setup-ngit`
(GitHub mirror `DanConwayDev/setup-ngit`).
## Query a result
```bash
ngit ci status <COMMIT|'#prefix'|nevent> --json # first query refreshes relays; no target means HEAD
ngit ci status <target> --json --offline # later cache-only reads
ngit ci status <target> --require-ci-trust maintainer-directed --json # exit non-zero unless green at this floor
```
Query the exact commit that introduced the change. A PR target reports only
its latest revision. Read:
- `ci.state`: pending, running, or concluded. `ci.conclusion` counts only
once the state is concluded.
- `ci.conclusion`: success, failure, cancellation, or another outcome.
`command_status: "ok"` means only that the query worked.
- `ci.runs[].workflow` and `ci.runs[].jobs`: which workflow and job passed or
failed.
- `ci.runs[].integrity`: the commit is present locally and the workflow hash
matches.
- `coverage` and each run's classification and evidence: how completely and
why the result is trusted. Partial coverage is not success.
If no run appears, report that no matching Nostr CI event was found. Then
check that the workflow existed at that commit, its trigger matched, a
coordinator serves the repository, and the query refreshed the relays before
concluding that CI did not run.
Trust floors are `maintainer-directed` and `operationally-associated`.
`ngit pr merge --require-ci-trust <LEVEL>` applies the same gate to a merge
when the caller wants one.
+103
View File
@@ -0,0 +1,103 @@
# Containers — publish OCI images
Read before publishing an OCI image, updating a container tag, choosing
Blossom storage, or constructing a gateway pull reference. Protocol
background: https://ngit.dev/protocol/software-publishing
## Model
`ngit container publish` (alias `ngit oci publish`) uploads the OCI blobs
reachable from the tagged entries of an OCI image layout to Blossom, then
signs a kind-30624 addressable event mapping tags to manifest digests. The
event is bound to the current kind-30617 git repository, so run it inside that
repository with a signer who is a confirmed maintainer. Gateways are read-only:
```bash
docker pull ncontainer.io/<npub>/<repository>:<tag>
```
## Publish
Export an OCI image layout first, for example
`podman push myimage oci:/tmp/myimage:latest`, then publish every tagged root:
```bash
ngit container publish myimage \
--layout /tmp/myimage \
--blossom-server https://blossom-one.example \
--blossom-server https://blossom-two.example \
--relay wss://relay.example \
--source https://example.com/myimage \
--json
```
A checked-in `.ngit/containers.yaml` lets `ngit container publish myimage
--json` select an entry:
```yaml
schema: 1
publication:
blossom_servers: [https://blossom-one.example, https://blossom-two.example]
relays: [wss://relay.example]
containers:
myimage:
layout: artifacts/myimage
source: https://example.com/myimage
```
Relative paths resolve from the repository root. `--manifest PATH` selects
another file; `--no-manifest` ignores the default and requires `--layout`. A
loaded manifest must define `NAME`. CLI layout and metadata override the
entry, a non-empty CLI Blossom list replaces the configured list, and CLI
relays extend configured relays. Signer selection, `--replace`, and output
mode stay on the command line.
Behaviour to know:
- `NAME` is one lowercase OCI repository-name component.
- Tags come from `org.opencontainers.image.ref.name` annotations in
`index.json`; filenames and git tags are irrelevant. `index.json` itself is
never uploaded; ngit merges the layout's tags into the tag map fetched from
the latest kind-30624 event.
- Without `--blossom-server`, ngit uses the publisher's latest kind-10063
server list. A single server means no redundancy. Every blob is checked on
every server, missing copies are uploaded with bounded retries and
verified, and the event is signed once each blob has at least one confirmed
copy; incomplete replication is reported per server.
- `--relay` extends the repository's relays; account and default relays are
not added. ngit reads the repository relays before and after uploading and
needs at least one success each time. A total preflight failure or a
concurrent-update refusal is safe to retry: uploaded blobs are
content-addressed. Keep a known state-bearing repository relay reachable
when changing relay sets, because a healthy empty relay cannot reveal an
event stranded elsewhere and a publish could then omit old tags.
## Merge versus replace
Ordinary publication updates the tags found in the new layout and retains
older tags, previous server hints, omitted metadata, and unknown event tags.
`--replace` publishes only the new layout's tags and selected servers, drops
omitted description, source, and unknown tags, and sets the title to
`--title` or `NAME`. Use it only when the user explicitly wants complete
replacement.
## JSON
A successful result has `command: "container.publish"`, a `warnings` array,
and `result` fields: `repository`, `git_repository`, `npub`, `name`, `naddr`,
`manifest_path` (or `null`), raw-hex `event_id` (unlike collaboration
commands' `nevent` ids), `tags` and `updated_tags`, per-blob SHA-256, size,
and per-server placement, final `blossom_servers`, and per-relay `accepted`.
Success means at least one relay accepted the event; inspect every
`result.relays[].accepted` when full fanout matters. Failures use
`command_status: "error"` with `error.details` holding per-blob and
per-server outcomes and possible orphan blobs.
## Limits
ngit accepts OCI and Docker v2 manifests and indexes using SHA-256, uploads
only blobs reachable from tagged roots, and rejects missing, oversized, deeply
nested, or mismatched graphs. It snapshots one blob at a time, so allow
temporary disk roughly equal to the largest layer. It does not build images,
push to registries, run a gateway, chunk layers, pull, list, or delete remote
blobs or tags. Software release assets use the separate `ngit release` model.
+29
View File
@@ -0,0 +1,29 @@
# Issues — create, view, comment, close
Read when working with issues. Guide: https://ngit.dev/issues
```bash
ngit issue create --subject "Bug title" --body "Details as markdown" --label bug --json
ngit issue list --json # add --status closed or --label bug to filter
ngit issue view <ID|nevent> --json --comments
ngit issue view <ID|nevent> --json --history # subject, cover-note, label, and status changes
ngit issue comment <ID|nevent> --body "Reproduced on v2.1" --json
ngit issue comment <ID|nevent> --body "Thanks!" --reply-to <comment-ID|nevent> --json
ngit issue close <ID|nevent> --reason "wontfix" --json
ngit issue resolved <ID|nevent> --reason "fixed in abc123" --json
ngit issue reopen <ID|nevent> --reason "regression in v2.3" --json
ngit issue label <ID|nevent> --label bug --label enhancement --json
ngit issue set-subject <ID|nevent> --subject "New title" --json
ngit issue set-cover-note <ID|nevent> --body "$(cat cover-note.md)" --json
```
`resolved` records that the problem was fixed; `close` records that it will
not be. Reference other events in `--body` as `nostr:nevent1…`.
## Auto-resolve from commits
A commit pushed to the declared default branch resolves an issue when its
message contains a form of `close`, `fix`, `resolve`, or `implement` followed
by a unique hex ID or prefix or a `nostr:nevent1…` reference, for example
`Fixes #deadbeef`. The status is published only when the pusher is the issue
author or a confirmed repository member.
+71
View File
@@ -0,0 +1,71 @@
# Nsites — publish static sites
Read before publishing an already-built website with `ngit nsite` or
diagnosing its Blossom uploads and NIP-5A manifest.
Guide: https://ngit.dev/releases/nsites (nsyte comparison, PR previews).
## Publish
Pass the build output directory, not the source tree. ngit uploads every
regular file, runs no build, applies no ignore files, and rejects symlinks,
unsafe paths, and filenames without extensions.
```bash
ngit nsite publish dist --json # reads nsyte's .nsite/config.json when present
ngit nsite publish dist --title "My site" --json # root kind-15128 site
ngit nsite publish dist --id docs \
--description-file site-description.txt \
--source "nostr://<npub>/<identifier>" \
--blossom-server https://blossom.example.com \
--blossom-server https://mirror.example.com \
--relay wss://relay.example.com --json # named kind-35128 site
ngit --signer <alias> nsite publish dist --json
ngit --nbunksec-file /run/secrets/publisher-nbunksec nsite publish dist --json # unattended NIP-46
```
- Config: `.nsite/config.json` (JSON, not YAML) fields `id`, `title`,
`description`, `source`, `fallback`, `servers`, and `relays` are read;
`--config PATH` selects another file and `--no-config` ignores it. Explicit
CLI values win, and any repeated `--blossom-server` or `--relay` replaces
that whole config array. Unsupported nsyte publication options
(`publishProfile`, `publishRelayList`, `publishServerList`,
`publishAppHandler`) produce a warning; nsyte signer fields are ignored.
- Metadata: `--title`, `--description` or `--description-file`, and
`--source` (`https://` or `nostr://`; omitted, ngit infers the selected
public repository and never a private one). NIP-5A has no logo tag; ship a
`favicon.ico` or `favicon.svg` in the build output.
- `--fallback SITE_PATH` (or config `fallback`) maps an existing HTML file in
the output to `/404.html` without another upload.
- Servers: omit `--blossom-server` to use the account's latest kind-10063
list; repeat it for replication. `--concurrency` (default 4, range 164) is
a global limit across presence checks and uploads.
## Guarantees
ngit snapshots the directory before network work, deduplicates content, checks
every blob on every selected server, and uploads missing copies with BUD-11
authorization. It signs the manifest only after every blob has at least one
confirmed copy, so a failed deployment cannot point the live manifest at
missing content. A server that fails three consecutive initial checks is
skipped for the rest of that pass while the others continue.
Rerun the same command after a failure: blobs already on a server are
confirmed with `HEAD` and skipped, so continuation is per blob and server. An
unchanged deployment reuses the current manifest without a new signature or
relay write.
## JSON
Check `command_status`, then:
- `result.changed`: publication versus an unchanged no-op;
- `result.config_path`, `result.fallback`, `result.relays`: resolved settings;
- `result.blossom.blobs[].servers[]`: each blob and server outcome;
- `result.publication.relays[]`: manifest acknowledgements (at least one
relay must accept);
- `warnings[]`: unknown MIME types, unsupported config publications, and
failed uploads or post-upload verification per server.
On a Blossom failure inspect `error.details.blobs` and
`error.details.possible_orphan_blobs`, fix the server or signer problem, and
rerun.
+96
View File
@@ -0,0 +1,96 @@
# Pull requests — open, update, stack, review, merge
Read before opening, updating, reviewing, or merging PRs.
Guide: https://ngit.dev/pull-requests
## Open or update a PR
The branch name MUST start with `pr/`. No push option turns another branch
into a PR.
```bash
git checkout -b pr/my-feature
git push -u origin pr/my-feature # one commit: its subject and body become title and description
git push -u origin pr/my-feature \
-o 'title=My feature' \
-o 'description=First paragraph.\n\nSecond paragraph.' # literal \n; push options cannot carry real newlines
git push -u origin pr/release-fix -o target-branch=release/2.x
git push -u origin pr/second-part -o base=<commit|branch|nevent> # pin or override the stack parent
git push --force origin pr/my-feature # update the PR after amending or rebasing
```
- `-d`/`--defaults` accepts the single-commit title and description without a
prompt.
- Do not use `$'…\n…'` for push options, and do not pre-escape a Markdown
file into `-o description=`; open the PR with `ngit send` instead.
- Stacks are inferred: a branch that contains the unique latest tip of one of
your other open or draft PRs becomes that PR's child and follows the parent
as it advances. Rebase the child onto the parent's latest tip before updating
it; ngit refuses stale children and ambiguous candidates rather than
guessing. Use `base=` for a cross-author, historical, or ambiguous parent,
and repeat it on each update if the child should stay pinned.
- To push as another stored identity, use
`git -c nostr.signer=<alias|npub|profile-name> push …`; `--signer` applies
to `ngit` commands only. `ngit account login --local <alias>` makes an
identity the repository default instead.
## ngit send
`ngit send` takes ordinary shell arguments, so `--description` accepts real
newlines from `$'…'` or `"$(cat file.md)"`. Inside double quotes `\n` stays a
literal backslash-n.
```bash
ngit send HEAD~2 --subject "My feature" --description "$(cat pr-description.md)" --json
ngit send HEAD~2 --in-reply-to <PR-ID|nevent> --json # new revision of an existing PR
ngit send --defaults --target-branch release/2.x --json
ngit send --defaults --base <commit|branch|nevent> --json
```
Do not also push a `pr/` branch for the same proposal.
## Read, comment, check out
```bash
ngit pr list --json # default filter: open,draft
ngit pr list --json --status open,draft,closed,applied --label bug
ngit pr view <ID|nevent> --json --comments
ngit pr comment <ID|nevent> --body "Looks good" --json
ngit pr comment <ID|nevent> --body "Fixed!" --reply-to <comment-ID|nevent> --json
ngit pr checkout <ID|nevent> --json # local tracking branch that git pull/push understand
```
## Merge (maintainer)
```bash
ngit pr merge <ID|nevent> --json # no-ff merge commit on the PR's target; does not push
ngit pr merge --json # PR inferred from the checked-out pr/ branch
ngit pr merge <ID|nevent> --require-ci-trust maintainer-directed --json # refuse unless CI is green at this trust floor
ngit pr merge <ID|nevent> --exclude-description --json # summary line and PR reference only
git push origin <target-branch> # publishes the merge and the applied status
```
`ngit merge` is a compatibility alias with the same options. The merge lands
on the PR's declared target, or the default branch, resolved against the
latest Nostr repository state rather than a local tracking ref, with the
message `Merge #<8-hex>: <PR title>`. Closed and applied PRs are refused
before any git change. On conflicts, resolve them and run `git commit`; the
message is already prepared, and JSON reports `action: "conflicted"` instead
of `"merged"`.
Before merging or adding maintainer fixes, run
`git log --merges --oneline origin/<target>..HEAD`. A prior `Merge #…` means a
merge would nest merge history; rebase or cherry-pick the PR commits onto the
current target first unless that history is intentional.
## Lifecycle
```bash
ngit pr close <ID|nevent> --reason "blocked by upstream" --json
ngit pr reopen <ID|nevent> --reason "fix was incomplete" --json
ngit pr ready <ID|nevent> --reason "addressed review feedback" --json
ngit pr draft <ID|nevent> --reason "needs more work" --json
ngit pr label <ID|nevent> --label bug --label enhancement --json
ngit pr set-subject <ID|nevent> --subject "New title" --json
ngit pr set-cover-note <ID|nevent> --body "Updated description. See nostr:nevent1abc…" --json
```
@@ -0,0 +1,119 @@
# Repositories — publish, clone, hosting, membership
Read when publishing or cloning a repository, resolving `nostr://` URL forms,
or changing an announcement's hosting, metadata, or roster. Guides:
https://ngit.dev/repositories (hosting choices, migrating from a forge,
mirrors, private repositories), https://ngit.dev/maintainers, and
https://ngit.dev/maintainers/going-deeper (leadless repositories, delegated
trust, removal, roster repair).
## URLs
```
nostr://<npub>/<identifier>
nostr://<npub>/<relay-hint>/<identifier> # relay-hint is a bare domain, e.g. relay.ngit.dev
nostr://<user>@<domain>/<identifier> # NIP-05, only when explicitly provided
nostr://<domain>/<repository-path> # NIP-AD: the full /path is sent URL-encoded to /.well-known/nostr.json?path=
```
## Clone
```bash
git clone nostr://<npub>/<relay-hint>/<identifier> # relay hint skips discovery
git clone nostr://<npub>/<identifier>
git clone nostr://user@domain.com/<identifier> # NIP-05, only if given to you
git clone nostr://ngit.dev/ngit.git # NIP-AD bare-domain path
```
Open and draft PRs are not fetched as branches unless `nostr.auto-pr-branches`
is `true`; `ngit pr checkout <ID|nevent>` materialises one on demand.
## Inspect
```bash
ngit repo --json --offline # run git fetch origin first when the cache may be stale
```
The output reports `nostr_url`, effective `git_servers`, `relays`, `hashtags`,
and `grasp_servers` detected from paired clone and relay entries, plus the
roster: `members`, `lead_source`, `lead_path`, `pending_actions`, and
`health`. Follow the actionable error or `pending_actions` rather than
replacing an announcement wholesale.
## Publish and host
`ngit init` declares the complete initial announcement. Grasp hosting supplies
both a git server and a relay; additional infrastructure is explicit, empty by
default, and supplements grasp hosting rather than replacing it:
```
relays = grasp-derived relays + additional relays
clones = grasp-derived clones + additional clones
```
```bash
ngit init --name "My Project" --description "What it does" --defaults --json # preferred grasp servers, else ngit defaults
ngit init --name "My Project" --grasp-server grasp.example.com --defaults --json
ngit init --name "My Project" --additional-relay wss://relay.example.com \
--additional-clone https://git.example.com/my-project.git --defaults --json
ngit init --name "My Project" --grasp-server "" \
--additional-relay wss://relay.example.com \
--additional-clone https://git.example.com/my-project.git --defaults --json # no grasp: both halves required
```
Every announcement needs at least one relay and one git server; `init` and
`repo edit` refuse to publish otherwise, including a metadata-only edit of an
announcement that already lacks one half. Repair it in the same command, e.g.
`ngit repo edit --name "New name" --add-grasp-server grasp.example.com`.
`--identifier` is set at initial publication only: changing the `d` tag
creates a different repository coordinate.
## Edit
`ngit repo edit` preserves omitted settings. Collections use targeted,
repeatable actions that can be combined in one command:
| Setting | Add | Remove |
| ------- | --- | ------ |
| Grasp server | `--add-grasp-server URL` | `--remove-grasp-server URL` |
| Additional relay | `--add-additional-relay URL` | `--remove-additional-relay URL` |
| Additional clone | `--add-additional-clone URL` | `--remove-additional-clone URL` |
| Hashtag | `--add-hashtag TAG` | `--remove-hashtag TAG` |
Scalars use replacement flags: `--name`, `--description`, `--web`, `--u`,
`--earliest-unique-commit`. A grasp-derived relay or clone cannot be removed
as an additional entry; remove the grasp server and its pair goes with it. To
empty a collection, remove every value currently reported.
Each successful edit publishes a fresh announcement and, when the repository
has Nostr state, republishes that state once so new relays and servers hold
the authoritative refs. If that fails, follow the reported `ngit sync`
recovery guidance.
## Roles and membership
- **Co-maintainer**: publishes git state, merges, manages issues and PRs, and
changes the roster at the protocol level.
- **Lead maintainer**: the same authority plus responsibility for the roster.
When a lead exists, ngit restricts routine roster changes to the lead
workflow.
- **Moderator**: publishes issue, PR, and patch status events (including
recording an existing merge) but cannot publish git state or merge.
Membership is reciprocal: a listing is an invitation until the invitee
publishes an announcement acknowledging the role, and an invited member's
events are not authoritative until then.
```bash
ngit repo edit --add-maintainer <npub> --json # a sole maintainer's first add makes them lead
ngit repo accept --json # invitee confirms the role and the lead; --grasp-server <url> also hosts the git data there
ngit repo follow-lead --json # members retain history and follow a changed lead or roster
ngit repo leave --json # end your own role and republish
ngit repo edit --remove-maintainer <npub> --json
ngit repo edit --lead-maintainer <npub> --json # handover: the new lead publishes the full roster first
ngit repo edit --acknowledge-maintainer-change <npub> --json
```
Change one relationship at a time. In the deliberately leadless case, pass
`--no-lead-maintainer` with every `--add-maintainer` or `--remove-maintainer`.
@@ -0,0 +1,49 @@
# Sync, flags, and configuration
Read when syncing refs, choosing flags, or tuning git config.
Guides: https://ngit.dev/configuration and https://ngit.dev/troubleshooting
## Sync
```bash
ngit sync --json # make git servers reflect the Nostr state for every ref
ngit sync --ref-name main --json # one ref
```
## Global flags
These accept any command position. `--offline` is per command; check
`ngit <command> --help`.
| Flag | Description |
| ---- | ----------- |
| `--json` | One JSON document on stdout (ngit commands only) |
| `-d`, `--defaults` | Non-interactive; accept defaults |
| `-q`, `--quiet` | Hide non-essential stderr progress (not combinable with `-v`) |
| `--repo <REMOTE\|NADDR\|NOSTR-URL>` | Select the target repository |
| `--signer <ALIAS\|NPUB\|NAME>` | Use a stored signer for this command |
| `--nsec-file`, `--nbunksec-file <PATH>` | One-shot key or bunker session from a private file (`--nsec`, `--nbunksec` take inline values) |
| `--repo-relay-only` | Publish only to repository relays |
| `-f`, `--force` | Bypass safety guards |
## git config
```bash
ngit --customize # list every option
git config nostr.signer alice # repository signer, including for git push
git config nostr.signer-alias.alice npub1... # portable alias-to-npub mapping
git config nostr.secret-storage file # auto | file | git-config
git config nostr.repo-relay-only true
git config nostr.auto-pr-branches true # fetch every open and draft PR as a pr/* branch (default false)
git config nostr.http-io-timeout-ms 600000 # allow large grasp pushes
NGIT_CACHE_DIR=/writable/path ngit repo --json # override the global event-cache directory
```
`nostr.auto-pr-branches` follows normal git config precedence. With the
default `false`, PRs appear as branches only after `ngit pr checkout`; run
`git fetch --prune` once to drop branches fetched by an older version, or use
`git clone --config nostr.auto-pr-branches=true <nostr-url>` to opt in from
the start.
If the global cache directory is unavailable ngit falls back to an in-memory
cache; the repository's git common directory must still be writable.
+2
View File
@@ -72,3 +72,5 @@ Use the committed scripts for cloud environments:
## Reproducibility
`Dockerfile` + `apkdiff.py` verify reproducible builds: `docker build -t amber-repro --build-arg VERSION=vX.Y.Z --build-arg APK_TYPE=free-arm64-v8a .` then `docker run --rm amber-repro` (expect `APKs match!`).
- For repository collaboration, use ngit and follow `.agents/skills/ngit/SKILL.md`.
+3 -1
View File
@@ -89,4 +89,6 @@ In other words, the lock controls who can open and navigate the app UI; it does
| `ConnectivityService.kt` | Foreground service, network monitoring, relay reconnection |
| `BiometricAuthScreen.kt` | UI-only app-launch lock (biometric/PIN); not a signing gate |
| `Biometrics.kt` | Wraps `BiometricPrompt` / keyguard credential prompt |
| `SecurityScreen.kt` | Toggles `useAuth` / `usePin` and the re-prompt interval |
| `SecurityScreen.kt` | Toggles `useAuth` / `usePin` and the re-prompt interval |
- For repository collaboration, use ngit and follow `.claude/skills/ngit/SKILL.md`.