← Back to list

I built a “Platformless” P2P distributed network and I don’t know what’s next.

Decided to work on a different project with AI

Alexander Wanyoike · 2026-06-10 17:49 · 1 claps · 17.1 min read
#software-engineering #decentralized-apps #p2p #rust #web-development
Open on Medium ↗
Wiki topics: AI · AI · General 🌐 · Web Development

I built a “Platformless” P2P distributed network and I don’t know what’s next.

Decided to work on a different project with AI

This is my first Medium post, so I am going to be honest up front: I am a bit embarrassed by this project.

Not because I think it is pointless. If I thought that, I would not be writing this. I am embarrassed because it is rough, ambitious, probably wrong in places, and built in public enough that the rough edges are easy to see. The code moved fast. The idea changed shape several times. Some parts work better than I expected, and some parts still feel like a prototype arguing with itself.

But that is also why I wanted to write it down now, before the project becomes either polished history or abandoned archaeology.

Platforms are everywhere now.

Facebook, X, Instagram, YouTube, TikTok, Substack, Twitch, Discord, Reddit. Different shapes, different cultures, different interfaces, but the same basic bargain: creators bring the content, users bring the attention, and the platform owns the room.

That bargain can work beautifully until it does not.

A platform gives you reach, discovery, payments, comments, followers, analytics, moderation tools, hosting, and a thousand invisible conveniences that would be painful to rebuild yourself. But it also puts your creative life behind someone else’s account system, ranking algorithm, moderation policy, monetisation rules, and export button. The audience may feel like yours, but in practice it lives inside a database you do not control.

This is not a niche problem. Pew Research Center reported in 2025 that 53% of U.S. adults at least sometimes get news from social media, with Facebook and YouTube alone used regularly for news by 38% and 35% of U.S. adults respectively. These are not just apps anymore. They are distribution infrastructure. They shape what people see, who gets heard, and which creators survive.

For creators, that creates a strange kind of dependency. You can spend years building an audience, only to wake up one day and discover that your reach has collapsed, your content has been demonetised, your account has been suspended, or the algorithm simply no longer likes the thing that made people follow you in the first place. In a CHI 2023 study on creator-friendly algorithms, researchers found that platform algorithms significantly affect creators’ creative choices and monetisation, and that opaque algorithmic and policy systems can distract creators from the actual work of creating.

That is the part that bothered me.

Not that platforms have algorithms. At their scale, they probably need them. The problem is that the algorithm becomes the weather. Creators do not negotiate with it. They adapt to it. They guess. They sacrifice. They optimise titles, thumbnails, posting schedules, formats, tone, and sometimes even their own beliefs around a machine they cannot inspect and a business model they do not control.

That is the Orwellian digital dystopia hiding inside the convenience.

Not a world where every platform is evil, or every algorithm is malicious, but a world where creators are constantly watched, ranked, nudged, rewarded, punished, demonetised, buried, or erased by systems they cannot meaningfully appeal to. The rules live somewhere else. The audience lives somewhere else. The archive lives somewhere else. Even the relationship between a creator and their followers is mediated through a feed the creator does not control.

So I started wondering what the opposite would look like. Not another social network. Not another “creator platform”. Not another place where you beg a central service to keep your account alive.

What would it look like if content distribution was not owned by the app at all? What if identity, publishing, discovery, retrieval, and relay infrastructure belonged to a network layer underneath applications? What if a creator could publish under an identity they control, and different apps could render that content in different ways without owning the creator, the audience, or the distribution channel?

That question became Jolt: a platformless peer-to-peer content syndication protocol.

The AI Part

There is another honest part of this story: I built almost all of this with AI.

Not “AI gave me a few snippets”.

I mean the protocol, daemon, Console, app permission model, packaging, update flow, Pastey, Spoke, debugging sessions, refactors, tests, and a lot of the documentation were built through a long AI-assisted development loop.

My role was not to disappear and let the machine invent a protocol.

I shaped the problem, reviewed the protocol model, challenged the boundary between the network and the applications, and kept pushing the project back toward the core idea: identity-owned distribution, not another platform with a different database. I reviewed the encryption mechanics, questioned whether private content really stayed private, and pushed on the difference between moving encrypted bytes through the network and granting someone plaintext access.

I also did the human testing the AI could not do by itself. I ran the Console, approved app sessions, created pastes and posts, tested Alice/Bob/Carol flows, watched propagation through local canaries, noticed when feeds were too slow, and called out when the system felt like eventual consistency wearing a web-app costume. Some of the most important decisions came from those sessions: receiver URLs felt wrong, polling felt wrong, recursive feed resolution was too slow, and the app-daemon boundary needed much more thought.

The AI wrote a large amount of the Rust, TypeScript, tests, GitHub Actions, Tauri packaging, and documentation. It also helped keep the work moving across multiple repositories without losing the thread. But the shape of Jolt came from a back-and-forth: AI implementation speed on one side, human protocol judgement and product pressure on the other.

That was both impressive and unsettling.

Impressive because this project moved much faster than it would have if I had written every line by hand. Unsettling because there were moments where the Rust code was doing things I understood conceptually, but would not have been able to write confidently from memory at that speed.

So this article is partly about Jolt, but it is also partly about what happens when a developer uses AI as an extremely fast implementation partner and then has to decide whether the thing that emerged is actually coherent.

That is why the project is in a freeze posture now.

Not because the code is done, but because the idea deserves review before more code gets piled on top.

The Inspiration

The core inspiration behind Jolt was simple: what if content was addressed to an identity, not trapped inside a platform?

On most platforms, your content lives at an address owned by the platform. A post is an Instagram post. A video is a YouTube video. A thread is an X thread. The URL, the account, the audience graph, the recommendation system, the moderation layer, and the archive all belong to the same central authority.

Jolt starts from a different premise.

An identity should be able to publish signed updates, and those updates should point to content. Applications should be able to read that content, render it, comment on it, reply to it, remix it, or ignore it, but they should not have to own the identity or the distribution channel.

That matters for creator independence, but it also matters in harsher environments. If content distribution depends entirely on a central platform, then an authoritative regime, hostile company, payment processor, app store, or infrastructure provider has a very obvious pressure point. Remove the account, remove the server, remove the app, remove the content.

Peer-to-peer systems do not magically solve censorship. That would be an overclaim. But they can change the shape of the problem. If content is signed by an identity, addressed by content hash, replicated by peers, cached by relays, and readable by multiple applications, then there is no single platform database that represents the whole public square.

The second inspiration was architectural.

Most modern applications are still built around a client-server model. The server owns the database. The client asks permission. The app developer becomes the platform operator, whether they wanted that job or not.

I wanted to see what it would feel like to build applications that take peer-to-peer networking seriously from the start. Not as an afterthought. Not as a sync feature bolted onto a SaaS product. But as the actual distribution model: identities publish, peers discover, relays help with reachability, and applications sit above the network instead of becoming the network.

That is the experiment Jolt is trying to run.

The Network Shape

At a high level, Jolt is not trying to be a website, a social network, or a hosted backend. It is trying to be the network layer underneath those things.

The user runs a local Jolt node. Applications talk to that node with scoped permission. The node owns the identity keys, signs updates, stores and fetches content, discovers peers, and optionally talks to relays or caches to improve reachability.

The important inversion is this: the application does not own the account.

The local node does.

That diagram is deliberately boring.

There is no global database in the middle. There is no canonical platform API that every app must obey. There is no feed service that decides what the network means. Jolt nodes exchange signed identity state and content-addressed objects. Applications decide how to interpret those objects.

In one app, the content might look like a social post. In another, it might be a gallery. In another, it might be a release note, a notebook entry, a podcast feed, a community page, or something I have not thought of yet.

That distinction matters because Jolt is not trying to decentralise one application. It is trying to decentralise the distribution substrate that applications can share.

The Content Model

Jolt has two core ideas at the content layer:

  1. Content is addressed by what it is.
  2. Mutable names are signed by who owns them.

The first part is content addressing. If you publish an object, Jolt stores it by content ID. The ID is derived from the content itself. If the bytes change, the content ID changes.

That is useful, but content addressing alone is not enough. Humans and apps need stable places to look. A creator does not want to say, “find my latest profile by guessing this new hash every time.” They want something closer to:

alice.jolt/profile -> CID
alice.jolt/spoke/feed -> CID
alice.jolt/releases/jolt-console -> CID

That is where signed update logs come in.

A Jolt identity can publish signed updates that map identity-owned paths to content IDs. The protocol-level statement is intentionally simple:

identity X maps path /some/path to content CID Y at sequence N

The update is signed by the identity owner. Other nodes can verify that the mapping was not invented by a relay, cache, app, or random peer.

This is the bit I like most about the model.

The content can move around the network, but the authority stays with the identity. A cache can store the bytes. A relay can help another node discover where to fetch them. A peer can replicate them. But none of those actors get to rewrite what Alice’s identity says /profile points to unless they can produce Alice's signature.

That gives Jolt a split between availability and authority. Availability can be shared. Authority remains local to the identity.

Publishing and Reading

The read path is also different from a normal platform app.

On a traditional platform, the client asks the platform server for a feed, profile, video, or post. The server looks in its database and returns whatever the platform decides the user should see.

In Jolt, an app asks the local daemon to resolve a Jolt address. The daemon looks for the identity’s signed update log, verifies it, finds the latest content ID for that path, and then fetches the content from wherever it can find it.

There are still hard problems here. The current v0 system is eventually consistent. Discovery is rough. The app-daemon interface probably needs to become more event-driven than the current REST-shaped API. Relays need clearer policy around pinning and abuse.

But the architecture is the experiment. Can apps be built as views over signed, identity-owned, peer-distributed content instead of becoming platforms themselves?

The P2P Implementation

Underneath the product language, Jolt’s networking layer is built on libp2p.

The node owns a libp2p swarm and combines a few focused behaviours instead of pretending that one generic “P2P network” solves everything.

There are separate request/response protocols for:

  • fetching content by content ID;
  • syncing an identity’s update log;
  • exchanging relay records, reachability hints, and identity provider candidates.

There is also Kademlia for provider discovery, identify for peer metadata, mDNS for local discovery, and an iroh transport path for the normal runtime. A TCP transport path exists mostly for deterministic local and namespace-based tests.

That split was important because Jolt has two different lookup problems:

  1. Who can provide this content ID?
  2. Who can provide the update log for this identity?

Those are not the same question.

If I have a content ID, I am looking for bytes. If I have a .jolt address, I first need the identity's signed update log so I can learn which content ID the path currently points to. Only after that can I fetch the content.

Relays in Jolt are optional infrastructure, not the centre of the system. They can help nodes find each other, exchange reachability records, forward provider queries, or keep authorized content available. But the relay does not become the platform. It should not be able to rewrite an identity’s signed update log, and it should not be treated as permanent public storage.

That is why pinning policy matters. If a relay is allowed to keep content available, it needs operator policy: who may pin, how much they may pin, how long content should stay, and what happens when limits are exceeded.

The current implementation works well enough to prove the model, but it also exposed one of the biggest v0 lessons: applications do not want to poll and guess forever. They want live local state. The next version of the app-daemon boundary probably needs subscriptions, materialized local views, or some other event-driven interface so apps can feel immediate while the network remains eventually consistent underneath.

Security and Encryption

The security model was one of the more interesting parts of the project.

Jolt is not trying to protect users by making every application trustworthy. That would be the wrong boundary. Apps are treated as clients that need scoped permission from the local daemon.

The daemon owns the sensitive authority:

  • identity signing keys;
  • signed update logs;
  • local content storage;
  • encryption and decryption;
  • app sessions and capabilities;
  • relay and pinning decisions.

Applications do not get raw identity keys. They request access. The user approves or rejects that request in Jolt Console. If approved, the app gets a session token with specific capabilities, such as publishing under a path it asked for. If the user revokes the session, the app loses that authority.

That local permission model matters because it keeps the protocol from turning every app into a wallet for your identity.

For public content, the security story is mostly about authenticity and integrity. A node can verify that an update came from the identity that owns the .jolt address. A node can also verify that fetched content matches its content ID. Relays and caches can help make content available, but they do not get to forge identity state.

Encrypted content adds another layer.

Pastey was the first app that forced this to become real. A public paste is easy: publish text, map a path to its CID, fetch it later. A private paste is different. The content still needs to move through the network, but only the intended recipients should be able to read the plaintext.

The shape we ended up with was an encrypted object envelope, but the important detail is the wrapped content key.

The daemon does not encrypt the same plaintext separately for Bob, Carol, and Dave. Instead, it generates one random content encryption key for the object, encrypts the plaintext once with that key, and then wraps that content key once per recipient.

That gives the envelope two layers:

  • the encrypted payload;
  • a recipient list containing wrapped copies of the content key.

Each recipient has a signed identity encryption key record. In v0, that means a separate X25519 encryption key published under the recipient’s identity and signed by their identity signing key. The author can resolve those records, verify them, and use them to wrap the content key for each intended recipient.

At a high level:

  1. The app asks the daemon to publish encrypted content for recipient identities.
  2. The daemon resolves each recipient’s encryption key record.
  3. The daemon generates a random content key.
  4. The daemon encrypts the plaintext once with that content key.
  5. The daemon wraps the content key separately for each recipient.
  6. The daemon includes an author/self wrap so the author can read their own object.
  7. The daemon signs the envelope with the author’s identity signing key.
  8. The encrypted envelope is stored and distributed like any other content object.
  9. A recipient fetches the envelope and asks their own local daemon to decrypt it.
  10. If the recipient has a matching wrapped content key, they get plaintext. If not, they only have ciphertext.

The important thing is that relays and caches only see encrypted bytes. They can help move the object around, but they do not need to know what is inside.

This was one of the moments where Jolt started to feel like more than a content addressing toy. Public publishing is useful, but private publishing forces the network to respect a much sharper boundary: availability can be public while readability stays private.

There is also recipient ingress, which is the beginning of two-way application communication. A sender can deliver an app-level envelope to another identity, but the recipient controls whether that ingress is pending, accepted, rejected, or ignored. That matters because “anyone can send me objects” is not a safe default unless the recipient has policy and control.

None of this means Jolt is security-reviewed. It is not. Jolt uses standard cryptographic ideas: identity signatures, content hashes, encrypted envelopes, scoped app sessions, and local key ownership. But a protocol can use good ingredients and still have bad composition. The right next step is a proper protocol and security review, not confidence theatre.

Try It Now

Jolt is still experimental, but there is now enough of a packaged path to try the idea without cloning the whole repository.

The current verified target is Linux AppImage. macOS and Windows are part of the direction, but Linux is the path that has actually been packaged and tested at the time of writing.

Install Jolt Console and the jolt daemon/CLI:

curl -fsSL https://raw.githubusercontent.com/alexanderwanyoike/jolt/main/scripts/install-jolt-console.sh | bash

Start the Console:

jolt-console

The Console is the local control surface. It starts and manages the daemon, shows daemon status, lets applications request scoped access, and lets the user approve, reject, or revoke app sessions.

Jolt works best as a local or manually configured network we currently have a default bootstrap node however if you are a technical user you can still run a bootstrap-capable relay node today.

Roughly, the bootstrap node setup looks like this:

# On a small VPS or always-on machine
curl -fsSL https://raw.githubusercontent.com/alexanderwanyoike/jolt/main/scripts/install-jolt-console.sh | bash -s -- --cli-only
export XDG_DATA_HOME=/var/lib/jolt-bootstrap
mkdir -p "$XDG_DATA_HOME/jolt"
cat > "$XDG_DATA_HOME/jolt/config.json" <<'JSON'
{
  "bootstrap_relays": [],
  "use_builtin_bootstrap_relays": false,
  "bootstrap_relay": true,
  "home_relay": null
}
JSON
jolt start --api-bind 127.0.0.1 --p2p-port 4001

Then run jolt status, copy one of the listening addresses that includes the peer ID, and share it with other nodes. Those other nodes can add it as a bootstrap relay through Jolt Console settings or with:

jolt bootstrap add /ip4/<host>/udp/<port>/quic-v1/p2p/<peer-id>

There are two small demo apps worth trying.

Pastey is the simpler one: a public and encrypted paste app. It uses Jolt to publish text under /pastes/*, fetch pastes by .jolt address or content ID, and encrypt private pastes for recipient identities.

Install Pastey:

curl -fsSL https://raw.githubusercontent.com/alexanderwanyoike/pastey/main/scripts/install-pastey.sh | bash

Start Pastey:

pastey

Pastey should request access through Jolt Console. Approve the request, then try publishing a public paste. If you have another Jolt identity available, you can also try an encrypted paste addressed to that recipient.

Spoke is the social proof-of-concept app built on top of Jolt:

curl -fsSL https://raw.githubusercontent.com/alexanderwanyoike/spoke/main/scripts/install-spoke.sh | bash

Start Spoke:

spoke

Spoke should request access through Jolt Console. Approve the request, then try publishing a profile or a post. Underneath that very ordinary-looking app flow, Spoke is not talking to a Spoke server. It is asking the local Jolt daemon to publish signed content under your identity. That is the point of both demos.

Pastey and Spoke are different applications with different product ideas, but they both ask the same local daemon for scoped authority. The app is replaceable. The identity is not supposed to be.

The Tradeoffs

Jolt is not a free lunch. Every design decision moves pain somewhere.

The first tradeoff is availability. A central platform is easy to reason about: if the platform is online and your account is allowed to exist, your content is available from the platform. Jolt does not have that central store. Content availability depends on your node, other peers, caches, or relays that choose to keep the content reachable. That makes the network less platform-dependent, but it also means availability becomes an explicit part of the system design.

The second tradeoff is consistency. Jolt v0 is eventually consistent. A published update may take time to show up elsewhere because another node has to discover the provider, fetch the signed update log, verify it, fetch the content, and update whatever app-level view is being rendered. That is a very different feeling from a normal web app backed by one database.

The third tradeoff is discovery. A platform gives you search, trending pages, recommendations, user directories, hashtags, and a million small discovery features. Jolt deliberately does not bake one global discovery product into the protocol. That keeps the network from becoming one platform wearing a protocol costume, but it means applications and communities still need to build discovery experiences on top.

The fourth tradeoff is moderation. If there is no central platform, there is no central moderation team that can make the entire network obey one policy. That can be good or bad depending on where you stand. Jolt can support application moderation, relay policy, local blocking, allowlists, deny lists, and community rules, but those are not the same thing as one company controlling the global feed.

The fifth tradeoff is abuse handling. Relays and caches are useful because they improve reachability and availability, but public infrastructure attracts spam, abuse, and resource exhaustion. Pinning cannot just be “anyone can store anything forever”. Relay operators need policy: size limits, rate limits, authorization, and probably allowlists for v0.

The sixth tradeoff is local complexity. In the platform model, the app hides the infrastructure from you. In Jolt, the local node is real. It owns keys, keeps state, runs a daemon, grants app permissions, and talks to the network. That is powerful, but it means the local runtime has to become boringly reliable before this can feel normal to non-technical users.

And finally, the security tradeoff is serious. Jolt uses standard cryptographic building blocks, but a working prototype is not the same thing as a reviewed protocol. Signed identity state, encrypted content envelopes, app permissions, relay hints, and recipient ingress all need proper security review before I would tell anyone to use this for sensitive real-world publishing. None of these tradeoffs kill the idea. They just define the work.

Where This Leaves Jolt

Jolt reached the point I wanted this experiment to reach.

It can publish content under a cryptographic identity. It can resolve signed identity-owned paths to content IDs. It can fetch content peer-to-peer. It can use relays and caches to improve reachability. It can approve external applications through a local Console. It can send recipient-controlled application messages. It can run a proof-of-concept social app without that app owning the user’s account.

That is enough to call v0 mildly successful. Not finished. Not production-ready. Not something I would pitch as the future of the internet with a straight face. But successful enough to take seriously somewhat.

The next step is not to add ten more features. The next step is to freeze the shape, review the protocol, optimize and harden the protocal, clean up the app-daemon interface, harden relay policy, write the v0 RFC, and build with it.

The show-stopping question is simple:

If Jolt does not make it meaningfully easier to build applications where users own their identity and distribution channel, then it belongs in the bin.

But if it does, even in a rough early form, then maybe platformless content distribution is worth exploring properly. That is why I built it. And that is why I am writing this down before deciding whether to keep going.

Sources


메타데이터
post_id
aab78a0fb320
slug
i-built-a-platformless-p2p-distributed-network-and-i-dont-know-what-s-next-aab78a0fb320
url
https://medium.com/@wanyoike.alexander/i-built-a-platformless-p2p-distributed-network-and-i-dont-know-what-s-next-aab78a0fb320
canonical_url
https://medium.com/@wanyoike.alexander/i-built-a-platformless-p2p-distributed-network-and-i-dont-know-what-s-next-aab78a0fb320
author_url
https://medium.com/@wanyoike.alexander
status
ok
fetched_at
2026-06-12 18:14:10