GhostWire is a decentralized, end-to-end encrypted messaging CLI built in Go. It combines Ethereum smart contracts (for routing and identity discovery) with IPFS (for encrypted file payload storage).
- Keep message transport decentralized by using on-chain events as the delivery bus.
- Keep message content private by encrypting data client-side before it reaches blockchain/IPFS.
- Separate lightweight metadata (on-chain) from heavy file content (IPFS).
- Use profile-based local state to support multiple identities on one machine.
- Profile-based setup with local config storage in
~/.ghostwire/<profile>. - Public-key registration on-chain (
X25519keys for message encryption). - End-to-end encrypted text messages.
- End-to-end encrypted file transfer:
- encrypted file bytes are pinned to IPFS,
- encrypted metadata (CID + symmetric key + filename) is sent on-chain.
- Trusted-address list for listener filtering.
- Historical catch-up + live subscription for incoming messages.
- Generated Go bindings for the Solidity contract.
- Identity layer
- You configure an Ethereum account (for tx signing) and generate an X25519 keypair (for message encryption).
- Registry layer (Ethereum contract)
- Users publish their public encryption key.
- Senders fetch recipient keys from the contract.
- Transport layer
- Text: encrypted payload is emitted directly in the
MessageSentevent. - File: encrypted file is stored in IPFS, and encrypted metadata is emitted on-chain.
- Text: encrypted payload is emitted directly in the
- Receive layer
- Listener filters by trusted addresses, decrypts incoming payloads, and stores files locally.
Contract: pkg/contract/GhostWire.sol
registerPublicKey(bytes32 _key)stores/updates sender public key.sendMessage(MessageInput calldata _msg)emits a message event.- Enforces:
- non-zero recipient,
- non-empty payload,
- recipient must be registered,
- max payload size = 5120 bytes.
cmd/- Cobra CLI commands.internal/blockchain/- Ethereum contract client operations.internal/encryption/- key generation + encrypt/decrypt logic.internal/ipfs/- IPFS upload/download client.internal/config/- profile config and local file persistence.pkg/contract/- Solidity source and generated Go bindings.
- Go
1.25+ - Access to Ethereum JSON-RPC endpoints:
- HTTP endpoint (transactions/calls),
- WebSocket endpoint (live event listening)
- IPFS pinning provider credentials:
- JWT token,
- private gateway URL,
- public pinning API URL
- (Optional)
solcandabigenif regenerating contract bindings
make encryption-test
make buildgo install github.com/niktin06sash/GhostWire/cmd/gw@latestgw initYou will be prompted for:
- profile name,
- RPC HTTP/WSS URLs,
- chain ID,
- contract address (optional if you plan to deploy),
- Ethereum private key (to pay for transactions),
- IPFS JWT and endpoints.
gw deploy -p <profile>gw register -p <profile>gw trust add <address> -p <profile>
gw trust list -p <profile>
gw trust remove <address> -p <profile>Text:
gw send <recipient-address> -p <profile> -m "hello"File:
gw send <recipient-address> -p <profile> -f ./path/to/filegw listen -p <profile>gw init- create/update profile config.gw deploy -p <profile>- deploy GhostWire contract and save address.gw register -p <profile>- generate local keypair and register public key on-chain.gw send <address> -p <profile> (-m <text> | -f <path_to_file>)- encrypt and send payload.gw trust ... -p <profile>- manage trusted sender list.gw listen -p <profile>- consume and decrypt incoming messages.
For each profile, data is stored under:
~/.ghostwire/<profile>/config.yaml- profile config.~/.ghostwire/<profile>/keys.json- X25519 keypair.~/.ghostwire/<profile>/<sender-address>/...- decrypted received files.
- Encryption is client-side (
nacl/boxfor metadata/text,nacl/secretboxfor file content). - Your Ethereum private key and encryption keys are stored locally (protect your machine).
- If keys are regenerated, previously encrypted messages/files become undecryptable.
- Trust list only filters what gets processed by listener; on-chain data is still public.
- Payload size on-chain is limited to
5 KB. - File content is not written on-chain; only encrypted metadata is.
- Contract emits events and does not persist message history in contract storage.
- Sending messages requires ETH in the balance to pay for gas.