enigma-memory 0.1.17 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/README.md +85 -27
  2. package/apps/cli/bin/enigma-desktop.mjs +140 -0
  3. package/apps/cli/bin/enigma-terminal.mjs +78 -0
  4. package/apps/cli/bin/enigma.mjs +4979 -3263
  5. package/apps/desktop/electron-main.cjs +217 -0
  6. package/apps/desktop/package.json +12 -0
  7. package/apps/desktop/src/app.js +264 -7
  8. package/apps/desktop/src/index.html +3514 -1373
  9. package/apps/desktop/src/launch-electron.mjs +51 -0
  10. package/apps/desktop/src/server.mjs +2914 -0
  11. package/apps/desktop/src/styles.css +2972 -260
  12. package/apps/desktop/src/zk-browser-prove.mjs +53 -0
  13. package/apps/desktop/src/zk-state.mjs +1789 -0
  14. package/apps/gateway/bin/enigma-gateway.mjs +102 -5
  15. package/apps/gateway/src/server.mjs +271 -8
  16. package/apps/ios/EnigmaCore/Package.swift +12 -0
  17. package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
  18. package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
  19. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
  20. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
  21. package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
  22. package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
  23. package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
  24. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
  25. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
  26. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
  27. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
  28. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
  29. package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
  30. package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
  31. package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
  32. package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
  33. package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
  34. package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
  35. package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
  36. package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
  37. package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
  38. package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
  39. package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
  40. package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
  41. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
  42. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
  43. package/apps/native-host/README.md +19 -8
  44. package/apps/native-host/bin/enigma-native-host.mjs +229 -13
  45. package/apps/relay/bin/enigma-relay.mjs +103 -5
  46. package/apps/relay/src/federation-runtime.mjs +618 -0
  47. package/apps/relay/src/server.mjs +310 -9
  48. package/apps/verifier/bin/enigma-verify.mjs +327 -11
  49. package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
  50. package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
  51. package/cortex-v3/circuits/build/vk_bytes.json +32 -0
  52. package/cortex-v3/proving-assets.json +64 -0
  53. package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
  54. package/cortex-v3/zk/action-transition-vk.json +119 -0
  55. package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
  56. package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
  57. package/cortex-v3/zk/intent-witness.mjs +365 -0
  58. package/cortex-v3/zk/intent-witness.test.mjs +485 -0
  59. package/cortex-v3/zk/proving-assets.mjs +203 -0
  60. package/cortex-v3/zk/sale-witness.mjs +783 -0
  61. package/cortex-v3/zk/sale-witness.test.mjs +784 -0
  62. package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
  63. package/cortex-v3/zk/settlement-evidence.mjs +722 -0
  64. package/cortex-v3/zk/setup-intent.mjs +688 -0
  65. package/cortex-v3/zk/setup-sale.mjs +666 -0
  66. package/cortex-v3/zk/setup.mjs +594 -0
  67. package/cortex-v3/zk/witness.mjs +184 -0
  68. package/cortex-v3/zk/zk-codec.mjs +232 -0
  69. package/cortex-v3/zk/zk-codec.test.mjs +293 -0
  70. package/cortex-v3/zk/zk-settle.mjs +370 -0
  71. package/cortex-v3/zk/zk-tree.mjs +256 -0
  72. package/cortex-v3/zk/zk-tree.test.mjs +419 -0
  73. package/deploy/SIMULATION.md +14 -9
  74. package/deploy/docker-compose.local-production-simulation.yml +54 -12
  75. package/docs/benchmark-attestation-network.md +487 -487
  76. package/docs/benchmark-reproducibility.md +289 -289
  77. package/docs/blockchain-only-mechanisms.md +400 -400
  78. package/docs/browser-extension-install.md +8 -6
  79. package/docs/client-connectors.md +16 -12
  80. package/docs/demo-proof-network.md +275 -275
  81. package/docs/developer-ecosystem.md +15 -13
  82. package/docs/developer-proof-quickstart.md +325 -325
  83. package/docs/enigma-memory-ready-conformance.md +378 -376
  84. package/docs/install-anywhere.md +64 -30
  85. package/docs/installers-and-desktop.md +8 -7
  86. package/docs/memory-benchmarks.md +1 -1
  87. package/docs/memory-drive-health-model.md +690 -690
  88. package/docs/novelty-invention-candidates.md +161 -161
  89. package/docs/proof-network-build-notes.md +240 -240
  90. package/docs/proof-network-claim-boundaries.md +320 -318
  91. package/docs/proof-network.md +339 -339
  92. package/docs/sdk-api.md +324 -324
  93. package/docs/solana-proof-rail.md +453 -453
  94. package/examples/01-quickstart-agent/index.mjs +49 -0
  95. package/examples/01_agent_memory_quickstart.mjs +57 -0
  96. package/examples/02-multi-agent-swarm/index.mjs +57 -0
  97. package/examples/02_cross_model_passport.mjs +64 -0
  98. package/examples/03-langchain-memory/index.mjs +41 -0
  99. package/examples/03_poseidon_commitment_verification.mjs +71 -0
  100. package/examples/04-python-trading-agent/trader.py +49 -0
  101. package/examples/README.md +27 -0
  102. package/examples/ci/github-actions.yml +7 -2
  103. package/package.json +410 -278
  104. package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
  105. package/packages/connectors/src/index.js +196 -4
  106. package/packages/connectors/swarm-router.mjs +168 -0
  107. package/packages/core/src/index.js +249 -2
  108. package/packages/core/src/version.mjs +7 -0
  109. package/packages/dev-tools/package.json +19 -0
  110. package/packages/dev-tools/src/index.js +4 -0
  111. package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
  112. package/packages/dev-tools/src/swarm-simulator.js +101 -0
  113. package/packages/dev-tools/src/vault-inspector.js +114 -0
  114. package/packages/dev-tools/src/vector-benchmark.js +100 -0
  115. package/packages/developer-platform/src/access-credentials.js +341 -0
  116. package/packages/developer-platform/src/http.js +132 -0
  117. package/packages/developer-platform/src/index.js +4 -0
  118. package/packages/developer-platform/src/usage-http.js +60 -0
  119. package/packages/developer-platform/src/usage.js +295 -0
  120. package/packages/enclave-runtime/attestation.mjs +159 -0
  121. package/packages/enclave-runtime/index.mjs +47 -0
  122. package/packages/enclave-runtime/session-manager.mjs +253 -0
  123. package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
  124. package/packages/enigma-reflex/package.json +14 -0
  125. package/packages/enigma-reflex/src/index.js +204 -0
  126. package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
  127. package/packages/enigma-reflex/training/requirements.txt +8 -0
  128. package/packages/enigma-reflex/training/train.py +314 -0
  129. package/packages/enigma-weave/LICENSE +22 -0
  130. package/packages/enigma-weave/UPSTREAM.json +21 -0
  131. package/packages/enigma-weave/package.json +14 -0
  132. package/packages/enigma-weave/src/index.js +286 -0
  133. package/packages/hosted-cloud/src/index.js +80 -5
  134. package/packages/importers/src/index.js +432 -0
  135. package/packages/inference-runtime/src/browser.js +401 -0
  136. package/packages/inference-runtime/src/chat.js +265 -0
  137. package/packages/inference-runtime/src/code.js +407 -0
  138. package/packages/inference-runtime/src/contracts.js +162 -0
  139. package/packages/inference-runtime/src/http.js +232 -0
  140. package/packages/inference-runtime/src/image.js +186 -0
  141. package/packages/inference-runtime/src/index.js +10 -0
  142. package/packages/inference-runtime/src/model-router.js +320 -0
  143. package/packages/inference-runtime/src/platform.js +125 -0
  144. package/packages/inference-runtime/src/privacy.js +400 -0
  145. package/packages/inference-runtime/src/video.js +253 -0
  146. package/packages/mcp-server/README.md +22 -6
  147. package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
  148. package/packages/mcp-server/src/index.js +2498 -1185
  149. package/packages/mcp-server/src/oauth.js +561 -0
  150. package/packages/mcp-server/src/private-handoff.js +84 -0
  151. package/packages/mcp-server/src/remote-http.js +273 -0
  152. package/packages/mcp-server/src/remote-policy.js +72 -0
  153. package/packages/mcp-server/swarm-bridge.mjs +361 -0
  154. package/packages/mesh/index.d.ts +283 -0
  155. package/packages/mesh/package.json +23 -0
  156. package/packages/mesh/src/crypto.js +189 -0
  157. package/packages/mesh/src/federation-packets.js +353 -0
  158. package/packages/mesh/src/gossip.js +311 -0
  159. package/packages/mesh/src/index.js +6 -0
  160. package/packages/mesh/src/protocol.js +255 -0
  161. package/packages/mesh/src/router.js +279 -0
  162. package/packages/mesh/src/transport.js +306 -0
  163. package/packages/passport/src/index.js +436 -7
  164. package/packages/private-economy/src/credits-http.js +100 -0
  165. package/packages/private-economy/src/credits.js +447 -0
  166. package/packages/private-economy/src/index.js +5 -0
  167. package/packages/private-economy/src/payments-http.js +120 -0
  168. package/packages/private-economy/src/payments.js +509 -0
  169. package/packages/private-economy/src/x402.js +346 -0
  170. package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
  171. package/packages/rag/index.d.ts +182 -0
  172. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
  173. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
  174. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
  175. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
  176. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
  177. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
  178. package/packages/rag/package.json +27 -0
  179. package/packages/rag/src/blinded-search.js +109 -0
  180. package/packages/rag/src/bm25.js +169 -0
  181. package/packages/rag/src/embeddings.js +459 -0
  182. package/packages/rag/src/hybrid.js +76 -0
  183. package/packages/rag/src/index.js +38 -0
  184. package/packages/rag/src/reranker.js +61 -0
  185. package/packages/rag/src/research.js +107 -0
  186. package/packages/rag/src/vector-store.js +430 -0
  187. package/packages/rag/src/verify-model-artifacts.mjs +4 -0
  188. package/packages/sdk/index.d.ts +760 -0
  189. package/packages/sdk/package.json +33 -0
  190. package/packages/sdk/python/README.md +24 -0
  191. package/packages/sdk/python/enigma_sdk.py +250 -0
  192. package/packages/sdk/python/pyproject.toml +34 -0
  193. package/packages/sdk/python/requirements.txt +1 -0
  194. package/packages/sdk/python/setup.py +20 -0
  195. package/packages/sdk/src/federation/capability-grant.js +389 -0
  196. package/packages/sdk/src/federation/federation-router.js +360 -0
  197. package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
  198. package/packages/sdk/src/federation/index.js +3 -0
  199. package/packages/sdk/src/index.js +1796 -0
  200. package/packages/sdk/src/intelligence/contradiction.js +337 -0
  201. package/packages/sdk/src/intelligence/decision-engine.js +155 -0
  202. package/packages/sdk/src/intelligence/index.js +4 -0
  203. package/packages/sdk/src/intelligence/ontology.js +122 -0
  204. package/packages/sdk/src/intelligence/temporal.js +123 -0
  205. package/packages/sdk/src/market-client.js +142 -0
  206. package/packages/sdk/src/mesh-client.js +110 -0
  207. package/packages/sdk/src/middleware/index.js +3 -0
  208. package/packages/sdk/src/middleware/langchain.js +159 -0
  209. package/packages/sdk/src/middleware/llamaindex.js +101 -0
  210. package/packages/sdk/src/middleware/vercel-ai.js +112 -0
  211. package/packages/sdk/src/rag-client.js +85 -0
  212. package/packages/sdk/src/swarm-orchestrator.js +260 -0
  213. package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
  214. package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
  215. package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
  216. package/packages/snapcompact/index.d.ts +284 -0
  217. package/packages/snapcompact/package.json +25 -0
  218. package/packages/snapcompact/src/index.js +716 -0
  219. package/packages/storage/PACKAGE_CONTRACT.md +1 -1
  220. package/packages/terminal-console/animations.mjs +240 -0
  221. package/packages/terminal-console/auto-anchor.mjs +220 -0
  222. package/packages/terminal-console/banner.mjs +91 -0
  223. package/packages/terminal-console/commands.mjs +459 -0
  224. package/packages/terminal-console/delegation.mjs +152 -0
  225. package/packages/terminal-console/index.mjs +5 -0
  226. package/packages/terminal-console/outbox.mjs +143 -0
  227. package/packages/terminal-console/phantom-bridge.mjs +637 -0
  228. package/packages/terminal-console/repl.mjs +136 -0
  229. package/packages/terminal-console/signer-store.mjs +130 -0
  230. package/packages/terminal-console/solana-rpc.mjs +214 -0
  231. package/packages/terminal-console/solana-transport.mjs +189 -0
  232. package/packages/terminal-tui/dashboard.mjs +214 -0
  233. package/packages/terminal-tui/index.mjs +28 -0
  234. package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
  235. package/packages/terminal-tui/telemetry-hud.mjs +137 -0
  236. package/packages/vault/index.d.ts +449 -0
  237. package/packages/vault/package.json +27 -0
  238. package/packages/vault/src/e2ee.mjs +393 -0
  239. package/packages/vault/src/enclave.js +481 -0
  240. package/packages/vault/src/erasure.js +207 -0
  241. package/packages/vault/src/index.js +1150 -125
  242. package/packages/vault/src/persistence.js +307 -0
  243. package/packages/vault/src/poseidon.js +354 -0
  244. package/packages/vault/src/receipt.js +459 -0
  245. package/scripts/benchmark-optical-context.mjs +166 -0
  246. package/scripts/bootstrap-enigma.mjs +502 -0
  247. package/scripts/build-edge-backend-workers.mjs +20 -5
  248. package/scripts/build-goal-completion-audit.mjs +72 -25
  249. package/scripts/build-hosted-api-key-lifecycle.mjs +292 -274
  250. package/scripts/build-hosted-customer-lifecycle.mjs +493 -476
  251. package/scripts/build-hosted-probe-worker.mjs +19 -4
  252. package/scripts/build-installer-assets.mjs +409 -389
  253. package/scripts/build-operator-evidence-starter.mjs +59 -1
  254. package/scripts/build-production-backend-env-kit.mjs +2 -0
  255. package/scripts/build-production-unblocker.mjs +4 -1
  256. package/scripts/build-proof-network-packet.mjs +213 -213
  257. package/scripts/check.mjs +25 -4
  258. package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
  259. package/scripts/install-enigma-local.mjs +18 -5
  260. package/scripts/release-audit.mjs +74 -115
  261. package/scripts/release-provenance.mjs +12 -2
  262. package/scripts/run-backend-readiness-smoke.mjs +112 -10
  263. package/scripts/run-standard-memory-benchmarks.mjs +1354 -1352
  264. package/scripts/scan-secrets.mjs +178 -0
  265. package/scripts/simulate-production-env.mjs +71 -10
  266. package/scripts/validate-hosted-backend-live.mjs +112 -1
  267. package/specs/antibody-pack-v1.schema.json +95 -0
  268. package/specs/antigen-envelope-v1.schema.json +81 -0
  269. package/specs/boundary-manifest-v1.schema.json +35 -35
  270. package/specs/capsule-v1.schema.json +55 -55
  271. package/specs/claim-boundary-manifest-v1.schema.json +22 -22
  272. package/specs/claim-ledger-v1.schema.json +291 -0
  273. package/specs/context-passport-v1.schema.json +59 -0
  274. package/specs/deletion-tombstone-v1.schema.json +26 -26
  275. package/specs/evidence-packet-v1.schema.json +177 -0
  276. package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
  277. package/specs/immune-scan-report-v1.schema.json +112 -0
  278. package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
  279. package/specs/memory-atom-v1.schema.json +59 -0
  280. package/specs/memory-event-v1.schema.json +42 -42
  281. package/specs/passport-v1.schema.json +50 -50
  282. package/specs/proof-of-non-use-v1.schema.json +65 -0
  283. package/specs/quarantine-record-v1.schema.json +126 -0
  284. package/specs/receipt-v1.schema.json +61 -61
  285. package/specs/state-checkpoint-v1.schema.json +37 -37
  286. package/specs/trust-bundle-v1.schema.json +56 -56
  287. package/specs/trust-card-v1.schema.json +119 -0
  288. package/docs/proof-network-launch-plan.md +0 -421
  289. package/packages/metering/PACKAGE_CONTRACT.md +0 -20
  290. package/scripts/build-ai-orchestration-plan.mjs +0 -248
@@ -0,0 +1,105 @@
1
+ import SwiftUI
2
+
3
+ struct OnboardingView: View {
4
+ @EnvironmentObject private var model: AppModel
5
+ @State private var server = ""
6
+ @State private var issuer = ""
7
+ @State private var clientID = ""
8
+ @State private var resource = ""
9
+ @State private var directToken = ""
10
+ @State private var showAdvanced = false
11
+
12
+ var body: some View {
13
+ ScrollView {
14
+ VStack(alignment: .leading, spacing: 26) {
15
+ Spacer(minLength: 24)
16
+ HStack(spacing: 10) {
17
+ EnigmaMark(size: 26)
18
+ Text("Enigma.").font(.system(.title2, design: .serif, weight: .bold))
19
+ }
20
+ VStack(alignment: .leading, spacing: 11) {
21
+ SectionEyebrow(text: "PRIVATE AI WORKSPACE")
22
+ Text("Your intelligence.\nYour boundary.")
23
+ .font(.system(size: 42, weight: .medium, design: .serif))
24
+ .tracking(-1.4)
25
+ Text("Connect to an Enigma server with OAuth PKCE. Conversation memory remains in this app process unless you explicitly save it.")
26
+ .font(EnigmaTheme.body)
27
+ .foregroundStyle(EnigmaTheme.secondary)
28
+ .fixedSize(horizontal: false, vertical: true)
29
+ }
30
+ VStack(alignment: .leading, spacing: 14) {
31
+ field("Enigma server", text: $server, prompt: "https://api.example.com")
32
+ field("OAuth issuer", text: $issuer, prompt: "https://auth.example.com")
33
+ field("OAuth client ID (optional)", text: $clientID, prompt: "auto-register when permitted")
34
+ field("Protected resource", text: $resource, prompt: "https://api.example.com")
35
+ Text("Leave client ID blank to request an approved public-client registration for this app.")
36
+ .font(.caption)
37
+ .foregroundStyle(EnigmaTheme.muted)
38
+ Button("Connect with OAuth PKCE") {
39
+ model.configure(server: server, issuer: issuer, clientID: clientID, resource: resource)
40
+ Task { await model.connectWithOAuth() }
41
+ }
42
+ .buttonStyle(PrimaryButtonStyle())
43
+ .disabled(model.isBusy || server.isEmpty || issuer.isEmpty || resource.isEmpty)
44
+ }
45
+ .enigmaSurface(padding: 18)
46
+
47
+ DisclosureGroup("Local development token", isExpanded: $showAdvanced) {
48
+ VStack(alignment: .leading, spacing: 12) {
49
+ Text("For a loopback development server only. The token is stored in Keychain and never written to UserDefaults.")
50
+ .font(.footnote)
51
+ .foregroundStyle(EnigmaTheme.secondary)
52
+ SecureField("Direct bearer token", text: $directToken)
53
+ .textInputAutocapitalization(.never)
54
+ .autocorrectionDisabled()
55
+ .padding(12)
56
+ .background(.white)
57
+ .overlay(RoundedRectangle(cornerRadius: 9).stroke(EnigmaTheme.line))
58
+ Button("Use direct token") {
59
+ model.configure(server: server, issuer: issuer, clientID: clientID, resource: resource)
60
+ Task { await model.connectWithToken(directToken) }
61
+ }
62
+ .buttonStyle(.bordered)
63
+ .disabled(server.isEmpty || directToken.isEmpty)
64
+ }
65
+ .padding(.top, 12)
66
+ }
67
+ .font(.subheadline.weight(.medium))
68
+
69
+ VStack(alignment: .leading, spacing: 6) {
70
+ privacyLine("No provider-native memory is treated as canonical.")
71
+ privacyLine("Smart Privacy runs on-device before external routes.")
72
+ privacyLine("OAuth tokens use Keychain with this-device-only access.")
73
+ }
74
+ Spacer(minLength: 24)
75
+ }
76
+ .padding(.horizontal, 24)
77
+ }
78
+ .onAppear {
79
+ server = model.serverAddress
80
+ issuer = model.oauthIssuer
81
+ clientID = model.oauthClientID
82
+ resource = model.oauthResource
83
+ }
84
+ }
85
+
86
+ private func field(_ label: String, text: Binding<String>, prompt: String) -> some View {
87
+ VStack(alignment: .leading, spacing: 6) {
88
+ Text(label.uppercased()).font(EnigmaTheme.label).tracking(1).foregroundStyle(EnigmaTheme.muted)
89
+ TextField(prompt, text: text)
90
+ .textInputAutocapitalization(.never)
91
+ .autocorrectionDisabled()
92
+ .keyboardType(.URL)
93
+ .padding(12)
94
+ .background(.white)
95
+ .overlay(RoundedRectangle(cornerRadius: 9).stroke(EnigmaTheme.line))
96
+ }
97
+ }
98
+
99
+ private func privacyLine(_ value: String) -> some View {
100
+ HStack(alignment: .top, spacing: 8) {
101
+ Text("·").foregroundStyle(EnigmaTheme.ember)
102
+ Text(value).font(.footnote).foregroundStyle(EnigmaTheme.muted)
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,275 @@
1
+ import EnigmaCore
2
+ import SwiftUI
3
+ import WebKit
4
+
5
+ struct PrivateVaultView: View {
6
+ @EnvironmentObject private var model: AppModel
7
+
8
+ var body: some View {
9
+ List {
10
+ Section {
11
+ HStack(spacing: 12) {
12
+ EnigmaMark(size: 30)
13
+ VStack(alignment: .leading, spacing: 2) {
14
+ Text("Private").font(.system(.title3, design: .serif, weight: .semibold))
15
+ Text("Owner-controlled memory and accountless credits").font(.caption).foregroundStyle(EnigmaTheme.muted)
16
+ }
17
+ }
18
+ .padding(.vertical, 8)
19
+ }
20
+ Section("Private economy") {
21
+ NavigationLink { CreditsView() } label: {
22
+ Label {
23
+ VStack(alignment: .leading) {
24
+ Text("Credits")
25
+ Text(model.creditsVault.map { "\($0.balance) private credits" } ?? "Create or recover a vault")
26
+ .font(.caption).foregroundStyle(EnigmaTheme.muted)
27
+ }
28
+ } icon: { Image(systemName: "wallet.pass") }
29
+ }
30
+ NavigationLink { UsageView() } label: {
31
+ Label("Usage without content", systemImage: "chart.bar.xaxis")
32
+ }
33
+ }
34
+ Section("Disposable tools") {
35
+ NavigationLink { BrowserToolView() } label: {
36
+ Label("Private browser", systemImage: "globe")
37
+ }
38
+ }
39
+ Section("Boundary") {
40
+ NavigationLink { PrivacyDetailView() } label: {
41
+ Label("Smart Privacy receipt", systemImage: "checkmark.shield")
42
+ }
43
+ NavigationLink { SettingsView() } label: {
44
+ Label("Connection settings", systemImage: "slider.horizontal.3")
45
+ }
46
+ }
47
+ }
48
+ .scrollContentBackground(.hidden)
49
+ .background(EnigmaTheme.canvas)
50
+ .navigationTitle("Private")
51
+ .task { await model.refreshCredits(); await model.refreshUsage() }
52
+ }
53
+ }
54
+
55
+ struct CreditsView: View {
56
+ @EnvironmentObject private var model: AppModel
57
+ @State private var recovery = ""
58
+
59
+ var body: some View {
60
+ ScrollView {
61
+ VStack(alignment: .leading, spacing: 18) {
62
+ VStack(alignment: .leading, spacing: 8) {
63
+ SectionEyebrow(text: "PRIVATE CREDITS")
64
+ Text("Accountless balance.\nRotating recovery.").font(EnigmaTheme.display)
65
+ Text("The credits ledger stores no email, wallet, or customer identity. Save each new recovery code offline.")
66
+ .font(.subheadline).foregroundStyle(EnigmaTheme.secondary)
67
+ }
68
+ HStack(spacing: 0) {
69
+ metric("BALANCE", value: model.creditsVault.map { "\($0.balance)" } ?? "0")
70
+ Divider().overlay(EnigmaTheme.line)
71
+ metric("AVAILABLE", value: model.creditsVault.map { "\($0.available)" } ?? "0")
72
+ }
73
+ .enigmaSurface(padding: 0)
74
+ if let code = model.recoveryCodeShownOnce {
75
+ VStack(alignment: .leading, spacing: 10) {
76
+ SectionEyebrow(text: "RECOVERY CODE — SHOWN ONCE")
77
+ Text("Store this offline. Successful recovery rotated the old code and revoked every prior credits access token.")
78
+ .font(.footnote).foregroundStyle(EnigmaTheme.secondary)
79
+ Text(code).font(.system(.caption, design: .monospaced)).textSelection(.enabled)
80
+ .padding(10).background(.white).overlay(RoundedRectangle(cornerRadius: 8).stroke(EnigmaTheme.line))
81
+ HStack {
82
+ Button("Copy") { UIPasteboard.general.string = code }
83
+ Spacer()
84
+ Button("Dismiss permanently", role: .destructive) { model.dismissRecoveryCode() }
85
+ }
86
+ .font(.footnote.weight(.semibold))
87
+ }
88
+ .enigmaSurface()
89
+ }
90
+ if model.creditsVault == nil {
91
+ Button("Create private vault") { Task { await model.createCreditsVault() } }
92
+ .buttonStyle(PrimaryButtonStyle())
93
+ }
94
+ VStack(alignment: .leading, spacing: 9) {
95
+ Text("RECOVER AND ROTATE").font(EnigmaTheme.label).foregroundStyle(EnigmaTheme.muted)
96
+ SecureField("enigma_cr_…", text: $recovery)
97
+ .textInputAutocapitalization(.never).autocorrectionDisabled()
98
+ .padding(12).background(.white).overlay(RoundedRectangle(cornerRadius: 9).stroke(EnigmaTheme.line))
99
+ Button("Recover vault") { Task { await model.recoverCreditsVault(recovery); recovery = "" } }
100
+ .buttonStyle(.bordered)
101
+ .disabled(recovery.isEmpty)
102
+ }
103
+ .enigmaSurface()
104
+
105
+ VStack(alignment: .leading, spacing: 8) {
106
+ SectionEyebrow(text: "FUNDING LINKABILITY")
107
+ Text(model.creditsVault?.paymentFundingBoundary ?? "Funding can be linkable: on-chain rails can reveal a wallet and Stripe can identify a customer. A separate payment ledger maps the funding event to the pseudonymous vault.")
108
+ .font(.footnote).foregroundStyle(EnigmaTheme.secondary)
109
+ if !model.creditPackages.isEmpty {
110
+ Text(model.creditPackages.map { "\($0.label): \($0.rails.joined(separator: ", "))" }.joined(separator: "\n"))
111
+ .font(.caption).foregroundStyle(EnigmaTheme.muted)
112
+ }
113
+ }
114
+ .enigmaSurface()
115
+ if model.creditsVault != nil {
116
+ Button("Forget this device session", role: .destructive) { model.forgetCreditsSession() }
117
+ .font(.footnote.weight(.semibold))
118
+ }
119
+ }
120
+ .padding(20)
121
+ }
122
+ .navigationTitle("Credits")
123
+ .navigationBarTitleDisplayMode(.inline)
124
+ .enigmaCanvas()
125
+ }
126
+
127
+ private func metric(_ label: String, value: String) -> some View {
128
+ VStack(alignment: .leading, spacing: 5) {
129
+ Text(label).font(EnigmaTheme.label).foregroundStyle(EnigmaTheme.muted)
130
+ Text(value).font(.system(.title, design: .serif, weight: .medium))
131
+ Text("private credits").font(.caption2).foregroundStyle(EnigmaTheme.muted)
132
+ }
133
+ .frame(maxWidth: .infinity, alignment: .leading)
134
+ .padding(16)
135
+ }
136
+ }
137
+
138
+ struct UsageView: View {
139
+ @EnvironmentObject private var model: AppModel
140
+
141
+ var body: some View {
142
+ ScrollView {
143
+ VStack(alignment: .leading, spacing: 18) {
144
+ SectionEyebrow(text: "30 DAY USAGE")
145
+ Text("Counts, never content.").font(EnigmaTheme.display)
146
+ if let usage = model.usage {
147
+ LazyVGrid(columns: [.init(.flexible()), .init(.flexible())], spacing: 10) {
148
+ usageMetric("EVENTS", "\(usage.totals.eventCount)")
149
+ usageMetric("INPUT", "\(usage.totals.inputUnits)")
150
+ usageMetric("OUTPUT", "\(usage.totals.outputUnits)")
151
+ usageMetric("KNOWN COST", (Double(usage.totals.knownCostMicrousd) / 1_000_000).formatted(.currency(code: "USD")))
152
+ }
153
+ Text(usage.costBoundary).font(.footnote).foregroundStyle(EnigmaTheme.secondary)
154
+ Label(usage.contentStored ? "Content stored" : "No prompts, outputs, or credentials stored", systemImage: usage.contentStored ? "exclamationmark.triangle" : "checkmark.shield")
155
+ .font(.footnote.weight(.semibold)).foregroundStyle(usage.contentStored ? .red : EnigmaTheme.success)
156
+ } else { ContentUnavailableView("No usage", systemImage: "chart.bar", description: Text("No metered events in this period.")) }
157
+ }
158
+ .padding(20)
159
+ }
160
+ .refreshable { await model.refreshUsage() }
161
+ .navigationTitle("Usage")
162
+ .navigationBarTitleDisplayMode(.inline)
163
+ .enigmaCanvas()
164
+ }
165
+
166
+ private func usageMetric(_ label: String, _ value: String) -> some View {
167
+ VStack(alignment: .leading, spacing: 5) {
168
+ Text(label).font(EnigmaTheme.label).foregroundStyle(EnigmaTheme.muted)
169
+ Text(value).font(.system(.title2, design: .serif, weight: .medium))
170
+ }.frame(maxWidth: .infinity, minHeight: 82, alignment: .leading).enigmaSurface()
171
+ }
172
+ }
173
+
174
+ struct PrivacyDetailView: View {
175
+ @EnvironmentObject private var model: AppModel
176
+
177
+ var body: some View {
178
+ List {
179
+ if let receipt = model.lastPrivacyReceipt {
180
+ Section("Last turn") {
181
+ LabeledContent("Mode", value: receipt.mode.rawValue.capitalized)
182
+ LabeledContent("Reference", value: String(receipt.receiptReference.uuidString.prefix(12)) + "…")
183
+ LabeledContent("Input shape", value: "\(receipt.inputCharacters) characters")
184
+ }
185
+ Section("Categories") {
186
+ ForEach(receipt.summary, id: \.self) { finding in
187
+ LabeledContent(finding.category.rawValue, value: "\(finding.action) × \(finding.count)")
188
+ }
189
+ }
190
+ } else { ContentUnavailableView("No receipt", systemImage: "checkmark.shield", description: Text("Send a protected chat turn first.")) }
191
+ Section { Text("Operational receipts contain category counts and a random reference—not prompt text, completion text, or a deterministic prompt hash.") }
192
+ }
193
+ .navigationTitle("Smart Privacy")
194
+ }
195
+ }
196
+
197
+ struct BrowserToolView: View {
198
+ @EnvironmentObject private var model: AppModel
199
+ @State private var address = "https://example.com"
200
+
201
+ var body: some View {
202
+ VStack(spacing: 12) {
203
+ HStack {
204
+ TextField("https://example.com", text: $address)
205
+ .textInputAutocapitalization(.never).autocorrectionDisabled().keyboardType(.URL)
206
+ .textFieldStyle(.roundedBorder)
207
+ Button("Go") { Task { await model.navigatePrivateBrowser(address) } }
208
+ .disabled(model.browserSession == nil)
209
+ }
210
+ .padding(.horizontal)
211
+ if let session = model.browserSession, let raw = session.streamUrl,
212
+ let stream = URL(string: raw), let expected = URL(string: model.browserViewerOrigin), sameOrigin(stream, expected) {
213
+ EphemeralBrowserView(url: stream, allowedOrigin: expected)
214
+ } else {
215
+ ContentUnavailableView(
216
+ "No private browser",
217
+ systemImage: "globe",
218
+ description: Text(model.browserViewerOrigin.isEmpty ? "Set the signed viewer origin in Connection settings first." : "Start a broker session after network-level egress proof is available.")
219
+ )
220
+ }
221
+ HStack {
222
+ Button("Start private session") { Task { await model.startPrivateBrowser() } }
223
+ .buttonStyle(.borderedProminent).disabled(model.browserViewerOrigin.isEmpty || model.browserSession != nil)
224
+ Button("Burn", role: .destructive) { Task { await model.burnPrivateBrowser() } }
225
+ .disabled(model.browserSession == nil)
226
+ }
227
+ .padding(.bottom)
228
+ }
229
+ .navigationTitle("Private browser")
230
+ .navigationBarTitleDisplayMode(.inline)
231
+ .enigmaCanvas()
232
+ .onDisappear { if model.browserSession != nil { Task { await model.burnPrivateBrowser() } } }
233
+ }
234
+
235
+ private func sameOrigin(_ left: URL, _ right: URL) -> Bool {
236
+ left.scheme == right.scheme && left.host == right.host && left.port == right.port && left.user == nil && left.password == nil
237
+ }
238
+ }
239
+
240
+ private struct EphemeralBrowserView: UIViewRepresentable {
241
+ let url: URL
242
+ let allowedOrigin: URL
243
+
244
+ func makeCoordinator() -> Coordinator { Coordinator(origin: allowedOrigin) }
245
+
246
+ func makeUIView(context: Context) -> WKWebView {
247
+ let configuration = WKWebViewConfiguration()
248
+ configuration.websiteDataStore = .nonPersistent()
249
+ configuration.defaultWebpagePreferences.allowsContentJavaScript = true
250
+ let view = WKWebView(frame: .zero, configuration: configuration)
251
+ view.navigationDelegate = context.coordinator
252
+ view.allowsLinkPreview = false
253
+ view.load(URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData))
254
+ return view
255
+ }
256
+
257
+ func updateUIView(_ view: WKWebView, context: Context) {
258
+ if view.url != url { view.load(URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData)) }
259
+ }
260
+
261
+ static func dismantleUIView(_ view: WKWebView, coordinator: Coordinator) {
262
+ view.stopLoading()
263
+ view.configuration.websiteDataStore.removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: .distantPast) {}
264
+ }
265
+
266
+ final class Coordinator: NSObject, WKNavigationDelegate {
267
+ let origin: URL
268
+ init(origin: URL) { self.origin = origin }
269
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
270
+ guard let url = navigationAction.request.url else { decisionHandler(.cancel); return }
271
+ let allowed = url.scheme == origin.scheme && url.host == origin.host && url.port == origin.port && url.user == nil && url.password == nil
272
+ decisionHandler(allowed ? .allow : .cancel)
273
+ }
274
+ }
275
+ }
@@ -0,0 +1,76 @@
1
+ import Foundation
2
+ import Security
3
+
4
+ struct OAuthCredential: Codable, Sendable {
5
+ let accessToken: String
6
+ let refreshToken: String?
7
+ let expiresAt: Date
8
+ let clientID: String
9
+ let issuer: URL
10
+ let resource: URL
11
+ }
12
+
13
+ enum SecureStoreError: LocalizedError {
14
+ case osStatus(OSStatus)
15
+
16
+ var errorDescription: String? {
17
+ switch self {
18
+ case let .osStatus(status): return "Keychain operation failed (\(status))."
19
+ }
20
+ }
21
+ }
22
+
23
+ struct SecureStore: Sendable {
24
+ let service: String
25
+
26
+ init(service: String = "com.enigmamemory.ios") {
27
+ self.service = service
28
+ }
29
+
30
+ func set(_ data: Data, account: String) throws {
31
+ let query: [String: Any] = [
32
+ kSecClass as String: kSecClassGenericPassword,
33
+ kSecAttrService as String: service,
34
+ kSecAttrAccount as String: account,
35
+ ]
36
+ SecItemDelete(query as CFDictionary)
37
+ var insertion = query
38
+ insertion[kSecValueData as String] = data
39
+ insertion[kSecAttrAccessible as String] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
40
+ let status = SecItemAdd(insertion as CFDictionary, nil)
41
+ guard status == errSecSuccess else { throw SecureStoreError.osStatus(status) }
42
+ }
43
+
44
+ func data(account: String) throws -> Data? {
45
+ let query: [String: Any] = [
46
+ kSecClass as String: kSecClassGenericPassword,
47
+ kSecAttrService as String: service,
48
+ kSecAttrAccount as String: account,
49
+ kSecReturnData as String: true,
50
+ kSecMatchLimit as String: kSecMatchLimitOne,
51
+ ]
52
+ var result: CFTypeRef?
53
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
54
+ if status == errSecItemNotFound { return nil }
55
+ guard status == errSecSuccess, let data = result as? Data else { throw SecureStoreError.osStatus(status) }
56
+ return data
57
+ }
58
+
59
+ func remove(account: String) throws {
60
+ let query: [String: Any] = [
61
+ kSecClass as String: kSecClassGenericPassword,
62
+ kSecAttrService as String: service,
63
+ kSecAttrAccount as String: account,
64
+ ]
65
+ let status = SecItemDelete(query as CFDictionary)
66
+ guard status == errSecSuccess || status == errSecItemNotFound else { throw SecureStoreError.osStatus(status) }
67
+ }
68
+
69
+ func setString(_ value: String, account: String) throws {
70
+ try set(Data(value.utf8), account: account)
71
+ }
72
+
73
+ func string(account: String) throws -> String? {
74
+ try data(account: account).flatMap { String(data: $0, encoding: .utf8) }
75
+ }
76
+ }
@@ -0,0 +1,60 @@
1
+ import SwiftUI
2
+
3
+ struct SettingsView: View {
4
+ @EnvironmentObject private var model: AppModel
5
+ @State private var server = ""
6
+ @State private var issuer = ""
7
+ @State private var clientID = ""
8
+ @State private var resource = ""
9
+ @State private var viewerOrigin = ""
10
+
11
+ var body: some View {
12
+ Form {
13
+ Section("Connection") {
14
+ field("Server", text: $server)
15
+ field("OAuth issuer", text: $issuer)
16
+ field("OAuth client ID (optional with approved registration)", text: $clientID)
17
+ field("Protected resource", text: $resource)
18
+ Button("Save connection") {
19
+ model.configure(server: server, issuer: issuer, clientID: clientID, resource: resource)
20
+ model.setBrowserViewerOrigin(viewerOrigin)
21
+ }
22
+ Button("Reconnect with OAuth PKCE") {
23
+ model.configure(server: server, issuer: issuer, clientID: clientID, resource: resource)
24
+ model.setBrowserViewerOrigin(viewerOrigin)
25
+ Task { await model.connectWithOAuth() }
26
+ }
27
+ }
28
+ Section("Private browser proof boundary") {
29
+ field("Signed viewer origin", text: $viewerOrigin)
30
+ Text("The app accepts only HTTPS stream URLs at this exact origin. The browser uses a non-persistent WKWebView, receives no Enigma OAuth token, rejects cross-origin top-level navigation, and burns on exit.")
31
+ .font(.footnote).foregroundStyle(EnigmaTheme.secondary)
32
+ }
33
+ Section("Custody") {
34
+ Label("OAuth and credits access tokens: Keychain, this device only", systemImage: "key.fill")
35
+ Label("Conversation: process memory only", systemImage: "memorychip")
36
+ Label("Recovery code: shown once, never saved automatically", systemImage: "doc.on.clipboard")
37
+ }
38
+ Section {
39
+ Button("Disconnect and clear OAuth credentials", role: .destructive) { Task { await model.disconnect() } }
40
+ }
41
+ }
42
+ .navigationTitle("Connection")
43
+ .onAppear {
44
+ server = model.serverAddress
45
+ issuer = model.oauthIssuer
46
+ clientID = model.oauthClientID
47
+ resource = model.oauthResource
48
+ viewerOrigin = model.browserViewerOrigin
49
+ }
50
+ }
51
+
52
+ private func field(_ label: String, text: Binding<String>) -> some View {
53
+ VStack(alignment: .leading, spacing: 5) {
54
+ Text(label).font(.caption).foregroundStyle(EnigmaTheme.muted)
55
+ TextField(label, text: text)
56
+ .textInputAutocapitalization(.never)
57
+ .autocorrectionDisabled()
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,80 @@
1
+ import SwiftUI
2
+
3
+ enum EnigmaTheme {
4
+ static let canvas = Color(red: 0.976, green: 0.969, blue: 0.949)
5
+ static let surface = Color.white.opacity(0.82)
6
+ static let ink = Color(red: 0.13, green: 0.12, blue: 0.105)
7
+ static let secondary = Color(red: 0.42, green: 0.40, blue: 0.36)
8
+ static let muted = Color(red: 0.58, green: 0.55, blue: 0.50)
9
+ static let line = Color(red: 0.87, green: 0.85, blue: 0.81)
10
+ static let ember = Color(red: 0.94, green: 0.30, blue: 0.11)
11
+ static let success = Color(red: 0.22, green: 0.45, blue: 0.31)
12
+
13
+ static let display = Font.system(.largeTitle, design: .serif, weight: .medium)
14
+ static let title = Font.system(.title2, design: .serif, weight: .medium)
15
+ static let body = Font.system(.body, design: .rounded)
16
+ static let label = Font.system(.caption2, design: .monospaced, weight: .semibold)
17
+ }
18
+
19
+ struct EnigmaCanvas: ViewModifier {
20
+ func body(content: Content) -> some View {
21
+ content
22
+ .foregroundStyle(EnigmaTheme.ink)
23
+ .background(EnigmaTheme.canvas.ignoresSafeArea())
24
+ .tint(EnigmaTheme.ember)
25
+ }
26
+ }
27
+
28
+ extension View {
29
+ func enigmaCanvas() -> some View { modifier(EnigmaCanvas()) }
30
+
31
+ func enigmaSurface(padding: CGFloat = 16) -> some View {
32
+ self.padding(padding)
33
+ .background(EnigmaTheme.surface)
34
+ .overlay(RoundedRectangle(cornerRadius: 12).stroke(EnigmaTheme.line, lineWidth: 0.8))
35
+ .clipShape(RoundedRectangle(cornerRadius: 12))
36
+ }
37
+ }
38
+
39
+ struct EnigmaMark: View {
40
+ var size: CGFloat = 22
41
+
42
+ var body: some View {
43
+ ZStack {
44
+ ForEach(0..<12, id: \.self) { index in
45
+ Capsule()
46
+ .fill(EnigmaTheme.ember)
47
+ .frame(width: size * 0.11, height: size * 0.44)
48
+ .offset(y: -size * 0.29)
49
+ .rotationEffect(.degrees(Double(index) * 30))
50
+ }
51
+ Circle().fill(EnigmaTheme.canvas).frame(width: size * 0.22, height: size * 0.22)
52
+ }
53
+ .frame(width: size, height: size)
54
+ .accessibilityHidden(true)
55
+ }
56
+ }
57
+
58
+ struct SectionEyebrow: View {
59
+ let text: String
60
+ var body: some View {
61
+ Text(text.uppercased())
62
+ .font(EnigmaTheme.label)
63
+ .tracking(1.6)
64
+ .foregroundStyle(EnigmaTheme.ember)
65
+ }
66
+ }
67
+
68
+ struct PrimaryButtonStyle: ButtonStyle {
69
+ func makeBody(configuration: Configuration) -> some View {
70
+ configuration.label
71
+ .font(.system(.subheadline, design: .rounded, weight: .semibold))
72
+ .frame(maxWidth: .infinity)
73
+ .padding(.vertical, 13)
74
+ .foregroundStyle(.white)
75
+ .background(EnigmaTheme.ember.opacity(configuration.isPressed ? 0.78 : 1))
76
+ .clipShape(RoundedRectangle(cornerRadius: 10))
77
+ .scaleEffect(configuration.isPressed ? 0.985 : 1)
78
+ .animation(.easeOut(duration: 0.12), value: configuration.isPressed)
79
+ }
80
+ }