Skip to content

Commit 2a83342

Browse files
committed
added guides
1 parent 3a375ff commit 2a83342

File tree

3 files changed

+833
-0
lines changed

3 files changed

+833
-0
lines changed

docs/website/api.html

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ <h3>Tagging</h3>
100100
<li><a href="#delete-tagging">Delete Object Tagging</a></li>
101101
</ul>
102102
</div>
103+
<div class="nav-section">
104+
<h3>Client-Side Encryption</h3>
105+
<ul>
106+
<li><a href="#encryption-headers">Encryption Headers</a></li>
107+
<li><a href="#metadata-privacy">Metadata Privacy</a></li>
108+
<li><a href="#secure-sharing-api">Secure Sharing</a></li>
109+
</ul>
110+
</div>
103111
</nav>
104112

105113
<main class="content">
@@ -949,6 +957,216 @@ <h3>Endpoint</h3>
949957
</div>
950958
</section>
951959

960+
<!-- Client-Side Encryption -->
961+
<section id="encryption-headers" class="endpoint">
962+
<div class="endpoint-content">
963+
<div class="description">
964+
<h2>🔐 Client-Side Encryption Headers</h2>
965+
<p>When using client-side encryption, Fula stores encryption metadata in custom headers.</p>
966+
967+
<h3>Encryption Metadata Header</h3>
968+
<p>The <code>x-amz-meta-encryption</code> header contains JSON with encryption info:</p>
969+
970+
<h3>Fields</h3>
971+
<ul>
972+
<li><strong>version</strong> - Encryption format version (currently 2)</li>
973+
<li><strong>algorithm</strong> - Cipher used (AES-256-GCM)</li>
974+
<li><strong>nonce</strong> - Base64-encoded nonce</li>
975+
<li><strong>wrapped_key</strong> - HPKE-encrypted DEK</li>
976+
<li><strong>metadata_privacy</strong> - Whether private metadata is included</li>
977+
<li><strong>private_metadata</strong> - Encrypted original filename, size, etc.</li>
978+
</ul>
979+
</div>
980+
<div class="example">
981+
<div class="example-header">
982+
<span class="lang-label">Encryption Metadata Structure</span>
983+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
984+
</div>
985+
<pre><code class="language-json">{
986+
"version": 2,
987+
"algorithm": "AES-256-GCM",
988+
"nonce": "base64_encoded_nonce",
989+
"wrapped_key": {
990+
"encapsulated_key": "base64_hpke_encapsulated_key",
991+
"ciphertext": "base64_encrypted_dek",
992+
"nonce": "base64_inner_nonce"
993+
},
994+
"metadata_privacy": true,
995+
"private_metadata": "{\"version\":1,\"ciphertext\":\"...\",\"nonce\":\"...\"}"
996+
}</code></pre>
997+
998+
<div class="example-header">
999+
<span class="lang-label">Head Request to Get Metadata</span>
1000+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
1001+
</div>
1002+
<pre><code class="language-bash"># Get metadata without downloading content
1003+
curl -I "http://localhost:9000/my-bucket/e/a7c3f9b2e8d14a6f" \
1004+
-H "Authorization: Bearer $TOKEN"
1005+
1006+
# Response headers include:
1007+
# x-amz-meta-encrypted: true
1008+
# x-amz-meta-encryption: {"version":2,"algorithm":"AES-256-GCM",...}
1009+
# Content-Type: application/octet-stream
1010+
# Content-Length: 156821 (ciphertext size)</code></pre>
1011+
</div>
1012+
</div>
1013+
</section>
1014+
1015+
<section id="metadata-privacy" class="endpoint">
1016+
<div class="endpoint-content">
1017+
<div class="description">
1018+
<h2>🔒 Metadata Privacy</h2>
1019+
<p>With metadata privacy enabled, the server never sees your real filenames, sizes, or content types.</p>
1020+
1021+
<h3>What Gets Obfuscated</h3>
1022+
<table style="width:100%; margin: 1rem 0; border-collapse: collapse;">
1023+
<tr style="background: var(--bg-tertiary);">
1024+
<th style="padding: 0.5rem; border: 1px solid var(--border-color);">Field</th>
1025+
<th style="padding: 0.5rem; border: 1px solid var(--border-color);">Server Sees</th>
1026+
<th style="padding: 0.5rem; border: 1px solid var(--border-color);">Client Decrypts</th>
1027+
</tr>
1028+
<tr>
1029+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);">Key (filename)</td>
1030+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);"><code>e/a7c3f9b2e8d14a6f</code></td>
1031+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);"><code>/finances/tax_2024.pdf</code></td>
1032+
</tr>
1033+
<tr>
1034+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);">Size</td>
1035+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);">156,821 (ciphertext)</td>
1036+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);">156,789 (original)</td>
1037+
</tr>
1038+
<tr>
1039+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);">Content-Type</td>
1040+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);"><code>application/octet-stream</code></td>
1041+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);"><code>application/pdf</code></td>
1042+
</tr>
1043+
<tr>
1044+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);">Timestamps</td>
1045+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);">Upload time only</td>
1046+
<td style="padding: 0.5rem; border: 1px solid var(--border-color);">Original created/modified</td>
1047+
</tr>
1048+
</table>
1049+
1050+
<h3>Private Metadata Structure</h3>
1051+
<p>The <code>private_metadata</code> field, when decrypted, contains:</p>
1052+
</div>
1053+
<div class="example">
1054+
<div class="example-header">
1055+
<span class="lang-label">Decrypted Private Metadata</span>
1056+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
1057+
</div>
1058+
<pre><code class="language-json">{
1059+
"original_key": "/finances/tax_returns_2024.pdf",
1060+
"actual_size": 156789,
1061+
"content_type": "application/pdf",
1062+
"created_at": 1701388800,
1063+
"modified_at": 1701475200,
1064+
"user_metadata": {
1065+
"author": "John Doe",
1066+
"department": "Accounting"
1067+
},
1068+
"content_hash": "blake3_hash_of_plaintext"
1069+
}</code></pre>
1070+
1071+
<div class="example-header">
1072+
<span class="lang-label">Key Obfuscation Modes</span>
1073+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
1074+
</div>
1075+
<pre><code class="language-plaintext"># DeterministicHash (default)
1076+
# Same path → same storage key (allows retrieval)
1077+
/photos/beach.jpg → e/a7c3f9b2e8d14a6f
1078+
1079+
# RandomUuid
1080+
# Each upload gets random key (maximum privacy)
1081+
/photos/beach.jpg → e/550e8400-e29b-41d4-a716-446655440000
1082+
1083+
# PreserveStructure
1084+
# Keep folder paths, hash only filenames
1085+
/photos/beach.jpg → /photos/e_a7c3f9b2e8d1</code></pre>
1086+
</div>
1087+
</div>
1088+
</section>
1089+
1090+
<section id="secure-sharing-api" class="endpoint">
1091+
<div class="endpoint-content">
1092+
<div class="description">
1093+
<h2>🤝 Secure Sharing API</h2>
1094+
<p>Share encrypted files without exposing your master key. Uses HPKE to re-encrypt the DEK for each recipient.</p>
1095+
1096+
<h3>Share Token Structure</h3>
1097+
<p>A share token is a JSON object containing:</p>
1098+
<ul>
1099+
<li><strong>share_id</strong> - Unique identifier (random 16-byte hex)</li>
1100+
<li><strong>path_scope</strong> - Folder path this share grants access to</li>
1101+
<li><strong>expires_at</strong> - Unix timestamp when share expires (optional)</li>
1102+
<li><strong>permissions</strong> - Read, write, delete flags</li>
1103+
<li><strong>encrypted_dek</strong> - HPKE-encrypted DEK for recipient</li>
1104+
<li><strong>owner_public_key</strong> - Owner's public key (for verification)</li>
1105+
</ul>
1106+
1107+
<h3>Permissions</h3>
1108+
<ul>
1109+
<li><code>read_only()</code> - Can decrypt and read files</li>
1110+
<li><code>read_write()</code> - Can read and upload new files</li>
1111+
<li><code>full_access()</code> - Can read, write, and delete</li>
1112+
</ul>
1113+
</div>
1114+
<div class="example">
1115+
<div class="example-header">
1116+
<span class="lang-label">Share Token JSON Structure</span>
1117+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
1118+
</div>
1119+
<pre><code class="language-json">{
1120+
"share_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
1121+
"path_scope": "/photos/vacation/",
1122+
"created_at": 1701388800,
1123+
"expires_at": 1702252800,
1124+
"permissions": {
1125+
"read": true,
1126+
"write": false,
1127+
"delete": false
1128+
},
1129+
"encrypted_dek": {
1130+
"encapsulated_key": "base64...",
1131+
"ciphertext": "base64...",
1132+
"nonce": "base64..."
1133+
},
1134+
"owner_public_key": "base64_x25519_public_key"
1135+
}</code></pre>
1136+
1137+
<div class="example-header">
1138+
<span class="lang-label">Sharing Workflow</span>
1139+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
1140+
</div>
1141+
<pre><code class="language-plaintext">┌─────────────────────────────────────────────────────────────────┐
1142+
│ SECURE SHARING FLOW │
1143+
├─────────────────────────────────────────────────────────────────┤
1144+
│ │
1145+
│ 1. OWNER creates share: │
1146+
│ • Specify path scope: /photos/vacation/ │
1147+
│ • Set expiry: 7 days │
1148+
│ • Set permissions: read-only │
1149+
│ • Re-encrypt DEK for recipient's public key │
1150+
│ │
1151+
│ 2. OWNER sends token to RECIPIENT: │
1152+
│ • Via email, message, QR code, etc. │
1153+
│ • Owner's private key never leaves device │
1154+
│ │
1155+
│ 3. RECIPIENT accepts share: │
1156+
│ • Verify token signature │
1157+
│ • Check expiry │
1158+
│ • Decrypt DEK with own private key │
1159+
│ │
1160+
│ 4. RECIPIENT accesses files: │
1161+
│ • Fetch encrypted files from server │
1162+
│ • Decrypt with the shared DEK │
1163+
│ • Path checked against scope │
1164+
│ │
1165+
└─────────────────────────────────────────────────────────────────┘</code></pre>
1166+
</div>
1167+
</div>
1168+
</section>
1169+
9521170
<footer>
9531171
<p>Fula API Documentation • Built with ❤️ for decentralized storage</p>
9541172
</footer>

0 commit comments

Comments
 (0)