Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Private Uploads

By default, all content uploaded to Arweave is public — anyone with the hash can read it. Private uploads let you store encrypted content permanently on Arweave where only authorized wallets can decrypt it.


How it works

Your content


Derive encryption key: sha256(walletPrivateKey + "inkd-private-v1")


AES-256-GCM encrypt with derived key


Upload ciphertext to Arweave   ← publicly stored, unreadable without key


on-chain: version.contentHash = ar:// of encrypted blob
          version.isPrivate   = true

What's on Arweave: only encrypted bytes. The plaintext never leaves your device.

What's on-chain: the Arweave hash of the encrypted content and an isPrivate flag — no plaintext, no key.

Who can decrypt: only the wallet that uploaded — the key is deterministically derived from the wallet private key.


Security model

ThreatProtection
Arweave node operatorSees only ciphertext
Inkd API serverReceives encrypted blob, never the key
Third-party observerAES-256-GCM ciphertext only
Unauthorized walletKey derived from owner's private key — unguessable without it
Key lossContent permanently inaccessible (no recovery)

Encryption: AES-256-GCM. Key derivation: sha256(walletPrivateKey + "inkd-private-v1").


Telegram Bot

The bot asks before every upload:

🔓 Public   🔒 Private
✖️ Cancel

Tap 🔒 Private — the bot encrypts using your bot-managed wallet key before upload. One tap, no extra steps.

For existing private projects, the bot shows a 🔓 View Content button in the project detail view. Tapping it decrypts the content on the fly using your wallet key and sends it back to you in the chat.


CLI

# Push a private version (encrypts before upload)
inkd version push \
  --id 10 \
  --file ./secret-model.bin \
  --tag v1.0.0 \
  --private

Output:

🔒 Private upload — content will be encrypted before Arweave storage
Encrypting and uploading ./secret-model.bin (14.2 KB)...
✓ Version v1.0.0 pushed! 🔒 Private
  Content (encrypted): ar://QmAbc123…
  Access manifest:     ar://QmDef456…
  TX:                  0xabc…
  Decrypt with:        inkd version decrypt --id 10 --index 0 --out ./decrypted

SDK

import { ProjectsClient } from "@inkd/sdk";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
import fs from "fs";
 
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const wallet  = createWalletClient({ account, chain: base, transport: http() });
 
const client = new ProjectsClient({
  wallet,
  privateKey: "0xYOUR_PRIVATE_KEY",   // required for private uploads
});
 
// Upload and register a private version
const result = await client.pushPrivateVersion(projectId, {
  content:     fs.readFileSync("./secret-model.bin"),
  tag:         "v1.0.0",
  contentType: "application/octet-stream",
});
 
console.log(result.txHash);       // on-chain TX
console.log(result.contentHash);  // ar:// of encrypted content
console.log(result.metadataHash); // ar:// of access manifest
console.log(result.isPrivate);    // true

Decrypt a private version

// Fetch and decrypt — only works if your wallet is in the access manifest
const plaintext = await client.decryptVersion(
  result.contentHash,   // ar:// of encrypted content
  result.metadataHash   // ar:// of access manifest
);
 
fs.writeFileSync("./decrypted.bin", plaintext);

Grant access to another wallet

// Add collaborator — they can now decrypt this project's versions
await client.addCollaborator(projectId, {
  address:           "0xCOLLABORATOR_ADDRESS",
  compressedPublicKey: "02abc...",   // secp256k1 compressed pubkey
  manifestArweaveHash: result.metadataHash,
});

Important notes

  • Key loss = permanent data loss. There is no recovery mechanism. If you lose access to your wallet, encrypted content is gone forever.
  • --private requires --file in the CLI. Pre-uploaded hashes cannot be retroactively encrypted.
  • Public and private versions can coexist in the same project — each version is independently public or private.
  • Collaborator management is per-project. Adding a collaborator uploads a new access manifest with their wrapped key.