New https://github.com/fiatjaf/nak release (v0.17.0) has:
- `nak git` commands that allow cloning, setting up a new nip34/grasp repository, pushing, fetching and pulling (just call "nak git push", for example, instead of "git push")
- `nak req --only-missing` flag that takes a jsonl file with events and does negentropy with a target relay to only download the events that are not in that file (finally nostr:npub1v0lxxxxutpvrelsksy8cdhgfux9l6a42hsj2qzquu2zk7vc9qnkszrqj49 -- this was ready 2 weeks ago, but I had to make a ton of git stuff before I was able to publish it)
- `nak serve --negentropy --blossom --grasp` new flags that make hosting these things of servers locally much easier for debugging
- you can finally use npubs/nprofiles/nevents/naddrs directly as arguments to `nak event`, `nak req` and others (they will be parsed and included in the event or filter as proper hex)
Login to reply
Replies (68)
Thank you nostr:nprofile1qyv8wumn8ghj76twvfhhstnjv4kxz7tn9ekxzmny9uq35amnwvaz7tms09exzmtfvshxv6tpw34xze3wvdhk6tcqyqalp33lewf5vdq847t6te0wvnags0gs0mu72kz8938tn24wlfze6luf5tq , appreciate the nak update ✌️
Nice
what timing. on the day that i finally really appreciated the thing! thanks!
the last bullet point is so good. i will say though that being forced to encode and decode was a good learning experience.
at least we get to say "back in my day" to the kids now
Thank you.
Nice. I was waiting for easy local debug blossom.
nostr:nprofile1qqstwptzcs6au74zrvmkku4hccp8r9t678ghqpgumkqphfzcgxh4u4spz4mhxue69uhhyetvv9ujuerpd46hxtnfduhszrnhwden5te0dehhxtnvdakz7qghwaehxw309aex2mrp0yh8qunfd4skctnwv46z77jas9x
```
kieran@kieran-x /m/k/c/nostr [1]> go install github.com/fiatjaf/nak@v0.17.0
go: github.com/fiatjaf/nak@v0.17.0 requires go >= 1.24.1; switching to go1.24.10
# fiatjaf.com/nostr/nip77
/home/kieran/go/pkg/mod/fiatjaf.com/nostr@v0.0.0-20251124002842-de54dd1fa4b8/nip77/nip77.go:113:6: wg.Go undefined (type "sync".WaitGroup has no field or method Go)
/home/kieran/go/pkg/mod/fiatjaf.com/nostr@v0.0.0-20251124002842-de54dd1fa4b8/nip77/nip77.go:122:6: wg.Go undefined (type "sync".WaitGroup has no field or method Go)
```
Golang is stupid, it compiles on my machine. Maybe it is using go1.25 to compile for me and not giving me this error.
Anyway, published v0.17.1 now that forces that version.
I was conflicted about adding it, but finally made my mind. I had enough of my own decoding learning experiences too.
Oh, it also support nip05 addresses.
Please report bugs (just replying in this thread is ok).
wat is GRASP?
i have started a negentropy peer implementation on orly now too, and it has a serve function that uses tmpfs, and blossom is default on, and respects whatever ACL system you enable. there is an aggregator tool that lets you trawl the nostrwebs for your events and writes them to jsonl. it has a GUI, which is still a bit of a WIP but already lets you log in, view and delete your own events. there is a zap-to-subscribe system for paid use case but it's untested as yet, same as blossom, though it's tested to the gills.
but wat is grasp?
Grasp is Blossom for git repositories.
You can host your repositories in multiple servers and their state is attested by Nostr events you publish that tells people where to fetch from and what is the latest head and branches, so the servers don't have to be trusted and it's easy to switch servers while keeping your repository identifiers immutable (they're essentially your pubkey + an arbitrary identifier).
Grasp servers can be self-hosted, paid, or ran for free by some community benefactor, and you can mix and use all these at the same time.
The existence of grasp servers also makes it easy for people to clone repositories, create branches and make them available to be merged by others, all without leaving the terminal (but of course there can also be all kinds of clients), which is the GitHub "pull request" UX people are familiar with.

Where is the spec for this?
https://gittr.space/npub1n2ph08n4pqz4d3jk6n2p35p2f4ldhc5g5tu7dhftfpueajf4rpxqfjhzmc/gittr?file=docs here's with the blossom addition
https://gitworkshop.dev/danconwaydev.com/grasp/tree/master/01.md
But I think it's still a work-in-progress. If you have some good ideas about how to best handle the pull request flow or other things let nostr:npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr know.
Can you voice your criticism in a clearer way? How does Nostr AUTH help with a git server? Or how is ssh better in any way than authorizing with Nostr then pushing with http?
No, hosting git objects on blossom is not easier than running a normal git server over HTTP, it would be ridiculously more complicated.
I don't understand what you're thinking.
The design for grasp is to authorise actions across nostr, not for particular relays one by one. This way we are minimising trust in relays and allow uses to mirror a repository fully without having to trust other relays / servers. The full authorisation lives fully in public.
well, it sounds like it needs a consensus then. but you'll see when you put it into operation.
sooner or later someone is going to have to break that ground in the land of nostr. i've got a plan but haven't had the time or priority for it yet.
once one functioning (maybe not perfect) consensus is operational on a test we will finally start to see what is really possible. it will be great. especially because it doesn't require a token. we have web of trust, this is going to go a long way
why does it need consensus? The state is embedded in an addrssable nostr event and you can probabilistically know whether the state you have is the latest based on your connectivity to relays and the trust trade-off you have with them that they are going to accept and timely serve you the maintainers events. This is a well established pattern in nostr.
it's got a liveness (weak availability) problem without a directory
What do you mean by a directory? Relay discovery for repositories?
coordination to ensure high consistency of the data
GRASP-02 (proactive sync) will sync git and nostr events related to accepted repositories from other repository relays / git server listed by maintainers in their announcement event.
that will help.
but it won't give us human readable names, which is kinda mandatory down the track if it will reach wide adoption.
Do you mean in relation to finding a repository? Nostr repositories use human readable repo identifier within a npub namespace. This leverages WoT based search. There is a lot of work to do make this work well, but I see a path to success for this.
yep, web of trust is the road to consensus on nostr. that's fundamental in the consensus protocol i have proposed, based on an experimental protocol that i have been watching the development of for the last 8 years.
so, as you can see, i have put a lot of thought into this and my questions demonstrated that this is not fully formed yet within this GRASP protocol, and other similar ideas.
i just recently learned, for example, that GrapeRank actually isn't being used anywhere yet (i've been working with its inventor the last week). we are right at the beginning of this process, but consensus is the place we are all heading.
Is your Grasp critique largely around repository discovery?
I think I addressed your concerns about auth and availability:
AUTH to nostr instead of AUTH to server.
GRASP-02 for proactive sync for better availablity (also client help to sync git data across grasp servers)
yep, discovery needs a consensus, just like discovery is needed for everything else, at a fundamental level, and why we are still so early.
nostr gives us the freedom to discover a new way to build a distributed consensus that is trustworthy and detached from mere economics. nostr is crowd-sourcing a robust, fair and just system of the delivery of messages between people. one without agendas. neutral.
there are quite a few people pondering hard on this, but the ones you read most often in trending are not. they don't even acknowledge the property rights of relay operators, or the potential for them to be faithful, to the degree that can't be avoided, to honoring their contracts with their customers.
nostr is just a hobby until it empowers a reduction of costs that couldn't be achieved any other way. that is the holy grail shining over the castle for me.
also, git repos need memorable names. that requires a consensus. making a note to add that to my consensus design
tbh I'm not all that happy that a human readable string is part of the repo id. even though there is a seperate 'name' for a repository, people still want to change the human readable bit of the ID and to do so are prepared to leave behind the repo and start again with a fresh ID.
Full Grasp support coming to BudaBit soon, the primitives are already implemented.
1. What we struggled with is the creation flow where we first post the repo announcement then the newly created git repo, but that must be done with an arbitrary sleep(X ms) wait time so that's not really ideal.
You guys have ideas to improve this flow?
2. I think it this was discussed before but want to get a fresh opinion of yall: Unified grasp api for file browsing and perhaps diffs and really data-heavy ops? Cloning repos via git smart http can still be a fallback but this would benefit performance a lot. Blossom has an api as well so I guess this would make sense, especially in a browser context.
nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gprfmhxue69uhhq7tjv9kkjepwve5kzar2v9nzucm0d5hszxmhwden5te0wfjkccte9emk2um5v4exucn5vvhxxmmd9us2xuyp nostr:nprofile1qqs2qzx779ted7af5rt04vzw3l2hpzfgtk0a2pw6t2plaz4d2734vngpz3mhxue69uhhyetvv9ujuerpd46hxtnfduqs6amnwvaz7tmwdaejumr0dsq3camnwvaz7tmgv9mx2m3wv3skucm0demkz7tyv4mzucm0d5p6s74j
Excellent work on v0.17.0, nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gprfmhxue69uhhq7tjv9kkjepwve5kzar2v9nzucm0d5hszxmhwden5te0wfjkccte9emk2um5v4exucn5vvhxxmmd9us2xuyp . The new nak git and debugging flags are going to be a massive time-saver for anyone building on Nostr. Appreciate the constant dedication to the tools! 🙏
nostr:nprofile1qqspa3z5wdxuhah72jgpecjup37xhjj7mky5gdqkwc06msep6wxlzwgpzdmhxw309ucnydewxqhrqt338gmnwdehqyxhwumn8ghj7mn0wvhxcmmvxznkfm
nostr:nevent1qqsymmnvxvnwu8jd22n4wkmuxehxjdp27gz36tkdawepp69rtakdnnqpr9mhxue69uhhq7tjv9kkjepwve5kzar2v9nzucm0d5pzqwlsccluhy6xxsr6l9a9uhhxf75g85g8a709tprjcn4e42h053vaqvzqqqqqqy79e92m
For 1, the 5s timeout used in ngit was more of a placeholder for a better approach. I think its best to pole the repo endpoint every 200ms until it reponds as if the repo as been created.
2. In the browser context we cannot rely on iso-morphic git as it doesn't support sparse clones and the UX of a shallow clone (with blobs) isn't good enough for large repos. eg. try browsing https://gitworkshop.dev/vitorpamplona.com/amethyst for the first time.
I think we should explore directly requesting a pack from the http endpoint containing the blobs we need (for a specific file) instead of relying on iso-morphic git. We could create a javascript library that does just this.
If this doesn't work then we should add an API endpoint for files / listing directories, etc. My concern about the API is 1) it enables the use of a grasp server as a CDN for files in a repository 2) where would we stop in terms of the API, there isn't a clear boundary and we could end of creating all git commands / options as an API which makes it a more complex protocol and harder to implement.
Someone nearly attempted to add sparse clone in iso-moprhic git but there are a baked in assumptions in may parts of the codebase that blobs are present so it would require a larger change and it might be hard to get merged as a first time contirbutor.
Good idea thanks!
I dug into this a while back and hacked together a poc implementation. I wanted to store ojbects in blossom so I extended nip-34 to include content addressable objects.
you probably shouldn't do this but there's no bad ideas, right?
**Standard NIP-34 repository state (kind 30618):**
```json
["refs/heads/main", "c8c4e344a9c0b4008bb72eebb188be7d7b83dcb1"]
```
**extension for content-addressed storage:**
```json
["ref", "heads/main", "c8c4e344a9c0b4008bb72eebb188be7d7b83dcb1", "9adca865e86f755634dc0e594b74ba7d6ff5a66e4070c278e29ecda1bf7f4150"]
["obj", "589d02c42ef724523ceba0697315e36520332993", "abc123def456789012345678901234567890abcdef123456789012345678901234"]
["obj", "e63987bfc58e1197df38e5edb1d9930eb08d8589", "def456abc789012345678901234567890123abcdef456789012345678901234567"]
```
**Extensions:**
- **4th parameter in `ref` tags**: Maps Git commit SHA to content-addressed storage hash (Blossom SHA-256)
- **`obj` tags**: Map Git object SHAs (trees, blobs) to their content-addressed storage hashes
Interesting. You'd end up with a lot of objects with that approach and eventually it would be too big for the event size. I thought about doing it with storing packs in blossom. Here is my code to play with that idea. https://gitworkshop.dev/danconwaydev.com/ngit/prs/note1s2au56ejtkfc5tqaduu2a6zp83xm80j2wmkjxx603y645jfrlq3qmp88s4 I would have made it into a POC if rust-nostr had blossom support at the time. It does now. It turns out that having a git server is way more flexible so ngit.dev/grasp came to be. Let git be git and let nostr be nostr.
nostr:nprofile1qqsf4qmhne6ssp2kcetdf4qc6q4y6lkmu2y2970xm545s7v7ey63snqpz9mhxue69uhkymmnw3ezuumgdacz7qg4waehxw309aex2mrp0yhxgctdw4eju6t09uqsuamnwvaz7tmwdaejumr0dshs7tcy97 are you worried that supporting so may ways of doing the same thing, adds unnecessary complexity, makes it harder to understand and harder to implement?
you could use Go and i already have a second draft blossom server written in go. i didn't write it. claude spun it up in about 3 hours and then another hour fixing it and i just haven't tested it yet. i know it works because it's just http and the tests pass, and i saw it accepting, and allowing me to delete a random blob several times. i just haven't used it. probably https://relay.orly.dev/ will already serve you with blossom. imma make sure you both have permissions in case you want to try it
my take on this is look into techniques used in computer games. i remember when GTA3 came out, and its most epic achievement was loading free inter-map transit. still very few games use this but it's a graph theory algorithm.
this is the kind of thing you need to automatically, and quickly partition a map of related data. you need metrics of proximity and some kind of parameters for partitioning the map to fit the compute you need to do.
it's not hard. but it may take a while to wrap your head around it. but graphs at high node count are N! style compute cost. so it only takes like 3 or 4 deep and you are practically at infinity as far as even the most powerful computers can do in milliseconds.
yeah youd bloat the event with every object ref as the repo grows. not a great design but a fun poc.
i wrote my poc in go. the code is actually hosted on itself, as the poc is a relay/blossom/webui all in one binary server. i also wrote my own git-nostr-remote for the client side. it was a fun hack and generally works for the happy path. no planning to pursue it. i can share the code if you’re interested in it.
nostr:nevent1qqsttu55rgfdjj9et04dzvv0ft4jytwtnl4n04th5yw6mn9pq7vdpngpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgxv3tql
consider the option of using REQ and subscriptions. they are so fast if clients randomly put my relay wss://relay.orly.dev/ later than other relays, it has already received it via subscription using the follows spider. i will follow up this note with a screenie of it happening, if it happens
nope, it didn't do it, lets see if it works this time
humbug, imwald is sending it to my relay first.
anyway, it happens now and then, i see in the upload popup on the bottom right, that if my relay doesn't get sent the event first, it returns that it already has it.
The problem is git enables many feature like shallow and parse cloning, packing specific object and data, getting specific files and git logs, etc. These are all battle tested on solid git server implementations. This is all not possible trying to reinvent a simplified git server with blossom.
Nice! Would love to see it.
i'm definitely with you on that. i personally would even suggest to not even write one single bit of code that handles git. palm it off to Linus' excellent implementation and then see what it misses.
i always envisioned that nostr was just negotiation rendezvous easy inbound connection. i've already tried to work with an attempt to clone the core features of git, in pure Go, and it was endlessly problematic with even minimal lag between their work and what the git project has already progressed to.
git is just a unix shell protocol, based on stdio and unix filesystem. don't over think it. the protocol only needs to provide the correct references and paths.
also, it's just combinatorial stuff. you can't efficiently deal with multidimensional graphs, you want to stick with stuff that can be flattened into a 2d representation. branches and layers are basically exponentially more complex.
git is built on the directed acyclic graph geometry. there is a lot of shortcuts because you don't have to escape loops.
The reference implementation does exactly that. It uses nginx to pass request to the git-http-backend CGI scripts maintained by the git project.
I'm sure the midcurve meme apply here with 'just let git handle that'.
It turns out that the http wrapper (CGI scripts) is not that complicated. There are nearly 3 grasp implementations that do this bit internally. This enables shipping a single binary and handling the authorisation *before* the data is sent.
i dream of a day, where other people understand that most things are just pipes and shells and access control. if you ever read me on a regular basis you kknow the last point is my biggest gripe with most devs, and i already have experienced first hand how hard it is to explain the first two to most devs.
networks are just pipes with extra steps.
you mean, the retard and the jedi saying that, of course.
yeah, don't reinvent the wheel if you don't have to. servers that translate between protocol and git is quite trivial to implement. this is not realtime, low latency requirements here. even as much as 5 seconds to lay down a commit and propagate it is fine.
never prematurely optimize, and don't roll your own if you can just assemble someone else's stuff into a shape that meshes with your actual part of it.
speaking from experience. even since picking up LLMs to help me with this, there are many things that just are not practical to do of such scale in any sane amount of time.
it was a harsh burn discovering that i couldn't do git stuff in pure go. the go version just isn't nearly adequate. it seemed to be working, and then i got all this mess going on. what was the error? i forget, it was some protocol network shit iirc. annoying af.
thats' why https://git.mleku.dev/mleku is just a plain gitea. i wanted to just host only my repos, and not have that stutter in the URL.
i spent probably weeks trying to get that working, and in the end, it was futile. linus seems to be turning into a javascript ninja with all his fucking breaking changes this last few years. fuck that guy.
just use his binary and interface to the git repo using it.
the bitch has got too complicated to build from scratch. oh, sure, we could do all kinds of things involving metadata on nostr and all that shit but you really should just pause, before you race off and do that, and go clone the git repo, and tell an LLM to explain it to you. 30 screenfuls of documentation later, you will be in agreement with me.
nah, just call git via your preferred language method for executing child processes. the end.
Not anylonger but it was certainly a fight. Made a PR on the optional changes back to gitnostr and a repo on snippets with which stuff got better to handle. And serious doku, not the unuseful kind but actually helpful 🐈⬛
If everyone contributes back and new ones still know where to start we' re good 🤗
Issues and PRs (kinds 9803/9804) are automatically published to nostr on handled status changes (merged, closed and reopened).
I fetch them from source if possible on import of the repo and try to aggregate those by their timestamps with the nostr kinds.
If source is lets say Github im not upstreaming the edits additionally there so far.
Anyway still needs polish in finding these kinds better and flows are surely not the endgame, but what i went with so far 🤓


I'm a bit confused. Are you are saying the extra complexity of having lots of different ways of doing the same thing is OK? And that you made a PR to a project that has only 1 commit posted 2 years ago.
Yes, thats what i am saying. Its about discoverability for me.
See nothing wrong in PRing changes so its up to date. Lost and left repos are a problem when ppl want to contribute.
complexity is a goldilocks thing. too simple and too complex are both bad. finding the balance is required. also, often times complexity can be tamed by subdividing the protocol or code into separate components that layer, this is the Unix maxim of "make it do one thing, and do it well" applied to architecture
Ist faster and working great, not sure what the problem is
The result is very reliable & fast so i dont really get the problem here neither with complexity and sharing snippets on that nor PRing progress. Ive contributed to much older code than 2 years before..


oh yeah, this is often called "composability" these days, and is the data model strategy used in Go with anonymous struct fields that overload a type with methods from multiple separate other types or interfaces.
Composition > Inheritance
it is related to the principles of Domain Driven Design also, which is about analysing your code to architect it as composable layers.
solves SO MANY BUGS just by applying this analysis to a codebase. almost half the time, i think, the worst bugs come from breaching interfaces and not using encapsulation, creating hidden bindings between two types that often you miss because you didn't look to see. haha.
Id like to know what's too complex in this. You can just fork it and build upon or just face the complexity.
That’s why the repo view is stitched together from tiny helpers with clear in/output instead of a Franken-class you can’t test. If theres still a hidden coupling, point it out and Id happily erase it.
I keep repo data via StoredRepo snapshots. The file fetch, renderer, and UI are orchestrated through small utilities and not by inheritance