Skip to content

Instantly share code, notes, and snippets.

@exonomyapp
Last active February 11, 2025 22:11
Show Gist options
  • Save exonomyapp/3d596106e3d486dc7b44fbf64b29128d to your computer and use it in GitHub Desktop.
Save exonomyapp/3d596106e3d486dc7b44fbf64b29128d to your computer and use it in GitHub Desktop.
Trusting Exonomy

Let's elaborate on the procedural steps of item 7. CID as a Trust Anchor, explaining the role of the "Master" JSON CID and how the CID of the initial signed manifest acts as a trust anchor. How is this manifest created, what's in it, and what happens when users pin this CID locally to ensure they’re referencing the correct root manifest. How are subsequent updates validated by checking the signature of newer manifests, especially when their CIDs change. No code samples but if that level of detail is useful, just narrate what will become code.


DeepSeek reasoning:

Okay, let's try to break down how the "Master" JSON CID works as a trust anchor. So, the user wants to make sure that even though the app can be updated and distributed peer-to-peer without central servers, there's a way to ensure that everyone is using a legitimate version. They mentioned using a JSON file that lists all the versions of the app, each with their own CID. The CID of this JSON file itself would be the trust anchor. But when you update the JSON file (like adding a new version), its CID changes. So how does that stay consistent as a trust anchor?

Hmm, the key here must be the initial CID. If the very first version of the JSON manifest has a CID that everyone agrees is the starting point, then any updates would need to be signed in a way that links back to that original CID. But how do you handle updates without relying on the original CID becoming outdated? Maybe the updates are chained, so each new manifest references the previous one, creating a verifiable history. But then each new manifest has its own CID. How do users know to trust the latest one?

Wait, the user also mentioned using a public key for the app. So maybe the initial JSON manifest is signed with the app's private key, and the public key is embedded in the app. When a new version of the manifest is created, it's also signed with the same private key. Even though the CID changes, the signature can be verified against the public key. So the trust comes from the cryptographic signature, not just the CID. The CID is just a pointer to the current manifest, but the signature ensures that the manifest hasn't been tampered with.

But then, how does a user get the initial public key? If the app is distributed peer-to-peer, the first time someone gets the app, they need a way to verify that the public key is legitimate. Maybe they have to get it from a trusted source in person, or through some out-of-band method. Once they have the public key, they can verify any subsequent manifests, even if the CIDs change, as long as the manifests are signed correctly.

So the "Master" JSON CID is the first version of the manifest that's signed and distributed. Users pin this CID locally, which means they always check against this specific CID to get the initial public key and the list of versions. But when there's an update, the new manifest would have a new CID, but it's signed by the same private key. The app could be designed to check for updates by looking at the latest signed manifest, verifying the signature with the embedded public key, and then trusting the new CID because the signature is valid.

Wait, but if the Master CID is pinned, how do users transition to trusting a new CID for the updated manifest? Maybe the initial manifest includes a mechanism for updates, like a field that points to the next manifest's CID, which is also signed. So each update is a linked list, where each manifest points to the next one, and all are signed. The user's app could follow this chain, verifying each step's signature, ensuring that even though CIDs change, the trust is maintained through the cryptographic signatures.

But this might get complicated with many updates. Alternatively, the Master CID could be considered the root, and any new manifest must be signed by the same key. The app could check the signature of any manifest it receives against the public key, regardless of the CID. So the CID is just a way to retrieve the manifest, but the actual trust comes from the signature. Pinning the Master CID is just the starting point to get the public key and initial versions. Once the public key is trusted, future manifests can be verified by their signatures, even if their CIDs are different.

So the process would be:

  1. The initial manifest (Master) is created, signed with the app's private key, and its CID is distributed as the trust anchor.

  2. Users download the app, which includes the public key. They use the Master CID to fetch the initial manifest and verify its signature with the public key.

  3. When an update is released, a new manifest is created with the new CIDs for the updated app versions. This new manifest is signed with the same private key.

  4. Users receive the new manifest (via P2P), verify the signature with the public key they already trust, and then trust the new CIDs listed in the updated manifest.

  5. The app can be designed to periodically check for new manifests, verify them, and update accordingly, all without needing a central server.

Potential issues include how users discover new manifests in a P2P network without central coordination. Maybe the app uses a distributed hash table (DHT) or some gossip protocol to propagate the latest manifest's CID. But since the signature is required, even if a malicious actor tries to spread a fake manifest, it won't have a valid signature and will be rejected.

Another consideration is key rotation. If the private key is compromised, a new key pair would need to be generated, and the Master manifest would need to be updated with the new public key, signed with the old private key. Users would then need to trust the new public key, perhaps through a manual update process or another out-of-band method.

In summary, the Master JSON CID serves as the initial trusted source of information, and cryptographic signatures ensure that updates are legitimate even as CIDs change. The trust is rooted in the public key, which is distributed with the app and verified against signed manifests. This allows for secure, decentralized updates without relying on a central authority.


DeepSeek response: Procedural Steps for Using a "Master" JSON CID as a Trust Anchor

1. Creation of the Initial Signed Manifest (Trust Anchor)

  • Purpose: Establish a cryptographically verified starting point for all app versions.
  • Process:
    • Generate Key Pair: During app development, create a cryptographic key pair (public/private keys). The private key is securely stored, and the public key is embedded into every app instance.
    • Build Initial Manifest: Create a JSON file (the "Master Manifest") listing:
      • App versions (e.g., Android, iOS binaries) with their CIDs.
      • The app’s public key (for verifying future updates).
      • Metadata (e.g., release notes, timestamp).
    • Sign the Manifest: Use the private key to generate a digital signature for the manifest.
    • Publish to IPFS: Upload the signed manifest to IPFS, generating its unique CID. This CID becomes the trust anchor.

2. Distribution of the Trust Anchor

  • Purpose: Ensure users can reliably access the initial manifest and verify its authenticity.
  • Process:
    • Embed in App: Include the Master Manifest’s CID and the public key in the app’s codebase.
    • Offline Sharing: Distribute the app and its trust anchor (CID) via P2P methods (e.g., Bluetooth, Wi-Fi Direct). Users receive the CID alongside the app binary.
    • First-Time Verification:
      • When a user installs the app, it fetches the Master Manifest using the pinned CID.
      • The app verifies the manifest’s signature using the embedded public key. If valid, the user trusts the listed app versions.

3. Validating Subsequent Updates

  • Purpose: Allow secure updates without relying on centralized servers.
  • Process:
    • New Version Release:
      • Update the JSON manifest with new app versions (e.g., Exonomy 2.0) and their CIDs.
      • Sign the updated manifest with the same private key.
      • Publish the new manifest to IPFS, generating a new CID.
    • Propagation:
      • Distribute the new manifest’s CID via P2P networks (e.g., mesh networks, local sharing).
      • Apps periodically check for updates by querying peers for the latest manifest CID.
    • Verification:
      • When an app retrieves a new manifest, it checks:
        1. Signature Validity: Uses the embedded public key to verify the manifest’s signature.
        2. Version Continuity: Ensures the update logically follows previous versions (e.g., version numbers increment).
      • If valid, the app trusts the new CIDs and downloads updated binaries.

4. Handling CID Changes and Key Rotation

  • Challenge: The Master Manifest’s CID changes with each update, but trust must persist.
  • Solution:
    • Signature-Based Trust: Users prioritize the cryptographic signature over the CID. Even if the CID changes, the signature ensures the manifest is authentic.
    • Linked Updates: Optionally, include a field in each manifest pointing to the CID of the previous version, creating a verifiable chain of updates.
    • Key Rotation:
      • If the private key is compromised, generate a new key pair.
      • Publish a final "revocation manifest" signed with the old key, instructing users to trust a new public key.
      • Distribute the new key via trusted channels (e.g., community consensus, in-person verification).

5. Offline-First Trust Maintenance

  • Purpose: Ensure functionality without internet access.
  • Process:
    • Local Pinning: Users pin the initial Master Manifest CID locally. The app prioritizes this CID for verification.
    • Peer-to-Peer Updates:
      • Users share updated manifests and app binaries directly (e.g., via local Wi-Fi networks).
      • Apps validate signatures offline using the embedded public key.
    • Fallback Mechanisms:
      • If a user cannot access the latest manifest, the app continues using the last verified version until a trusted update is found.

Risks and Mitigations

  • Risk: Initial Trust Bootstrap
    Mitigation: Use social trust (e.g., in-person sharing, community leaders) to distribute the Master Manifest CID and public key.

  • Risk: Key Compromise
    Mitigation: Implement multi-signature schemes or hardware security modules (HSMs) to protect the private key. Use revocation manifests for emergency key rotation.

  • Risk: Update Discovery
    Mitigation: Leverage decentralized protocols (e.g., IPFS PubSub, gossip networks) to propagate new CIDs efficiently across P2P networks.


Summary

The "Master" JSON CID acts as a cryptographic root of trust by:

  1. Anchoring Trust: The initial CID provides a fixed reference point for verifying the first signed manifest.
  2. Enabling Updates: New manifests, though with different CIDs, inherit trust through consistent cryptographic signatures.
  3. Decentralizing Validation: Users verify updates offline using the embedded public key, eliminating reliance on central servers.

This system balances decentralization with security, allowing Exonomy to scale across millions of users while resisting hijacking and ensuring offline resilience.

Using protocol-based filtering in IPFS is a powerful way to ensure that only peers supporting a specific protocol (e.g., /exonomy/1.0.0) can communicate with your node. This prevents unauthorized peers from interacting with your application, effectively securing your namespace.

Let’s break down how to implement protocol-based filtering in IPFS, including how to reject peers that don’t support the protocol.


Protocol-Based Filtering

How to use IPFS's protocol-based filtering with code samples, including how to reject peers that don't support a given protocol.

Key Concepts

  1. Protocols in libp2p:

    • In IPFS, communication between peers is handled by libp2p, which supports multiple protocols.
    • A protocol is a string identifier (e.g., /exonomy/1.0.0) that defines how peers communicate.
  2. Protocol-Based Filtering:

    • You can configure your node to only connect to peers that support a specific protocol.
    • Peers that don’t support the protocol are rejected.
  3. Rejecting Unauthorized Peers:

    • When a peer connects, you can check if it supports the required protocol.
    • If it doesn’t, you can disconnect from the peer.

Implementation Steps

1. Define the Custom Protocol

Define the protocol that your app will use (e.g., /exonomy/1.0.0).

const exonomyProtocol = "/exonomy/1.0.0";

2. Configure libp2p to Use the Protocol

When initializing your IPFS node, configure libp2p to support the custom protocol.

const IPFS = require("ipfs");

const node = await IPFS.create({
  libp2p: {
    modules: {
      transport: [], // Add your transport modules here
      streamMuxer: [], // Add your stream muxer modules here
      peerDiscovery: [], // Add your peer discovery modules here
    },
    config: {
      peerDiscovery: {
        [exonomyProtocol]: {
          enabled: true,
        },
      },
    },
  },
});

3. Listen for Peer Connections

Use libp2p’s event system to listen for peer connections and check if they support the protocol.

node.libp2p.on("peer:connect", (connection) => {
  const peerId = connection.remotePeer.toB58String();
  const peer = node.libp2p.peerStore.get(peerId);

  // Check if the peer supports the Exonomy protocol
  if (!peer.protocols.includes(exonomyProtocol)) {
    console.log(`Peer ${peerId} does not support the Exonomy protocol. Disconnecting...`);
    node.libp2p.hangUp(peerId); // Disconnect from the peer
  } else {
    console.log(`Peer ${peerId} supports the Exonomy protocol. Allowing connection.`);
  }
});

4. Reject Unauthorized Peers

If a peer doesn’t support the protocol, disconnect from it using hangUp.

node.libp2p.hangUp(peerId);

5. Broadcast the Protocol to Peers

When connecting to peers, ensure that your node advertises the Exonomy protocol.

node.libp2p.peerStore.on("peer:connect", (peerId) => {
  const peer = node.libp2p.peerStore.get(peerId);
  peer.protocols.push(exonomyProtocol); // Advertise the Exonomy protocol
});

Full Example Code

Here’s the complete code for implementing protocol-based filtering in IPFS:

const IPFS = require("ipfs");

// Define the custom protocol
const exonomyProtocol = "/exonomy/1.0.0";

// Create an IPFS node with libp2p configured to use the custom protocol
const node = await IPFS.create({
  libp2p: {
    modules: {
      transport: [], // Add your transport modules here
      streamMuxer: [], // Add your stream muxer modules here
      peerDiscovery: [], // Add your peer discovery modules here
    },
    config: {
      peerDiscovery: {
        [exonomyProtocol]: {
          enabled: true,
        },
      },
    },
  },
});

// Listen for peer connections
node.libp2p.on("peer:connect", (connection) => {
  const peerId = connection.remotePeer.toB58String();
  const peer = node.libp2p.peerStore.get(peerId);

  // Check if the peer supports the Exonomy protocol
  if (!peer.protocols.includes(exonomyProtocol)) {
    console.log(`Peer ${peerId} does not support the Exonomy protocol. Disconnecting...`);
    node.libp2p.hangUp(peerId); // Disconnect from the peer
  } else {
    console.log(`Peer ${peerId} supports the Exonomy protocol. Allowing connection.`);
  }
});

// Advertise the Exonomy protocol to peers
node.libp2p.peerStore.on("peer:connect", (peerId) => {
  const peer = node.libp2p.peerStore.get(peerId);
  peer.protocols.push(exonomyProtocol); // Advertise the Exonomy protocol
});

console.log("IPFS node is running with protocol-based filtering.");

How It Works

  1. Custom Protocol:

    • The Exonomy protocol (/exonomy/1.0.0) is defined and used for communication.
  2. libp2p Configuration:

    • The IPFS node is configured to support the custom protocol.
  3. Peer Connection Handling:

    • When a peer connects, the node checks if the peer supports the Exonomy protocol.
    • If the peer doesn’t support the protocol, it is disconnected.
  4. Protocol Advertisement:

    • The node advertises the Exonomy protocol to other peers.

Benefits of Protocol-Based Filtering

  • Security: Only peers supporting the Exonomy protocol can connect to your node.
  • Control: You can define and enforce custom communication rules.
  • Scalability: Works well for large networks with millions of peers.

Conclusion

By using protocol-based filtering in IPFS, you can ensure that only legitimate Exonomy nodes can communicate with your application. This prevents unauthorized peers from hijacking your namespace and ensures a secure and reliable network.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment