vayl-mcp 0.1.0__tar.gz

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 (96) hide show
  1. vayl_mcp-0.1.0/COMPLIANCE.md +222 -0
  2. vayl_mcp-0.1.0/CONTRIBUTING.md +38 -0
  3. vayl_mcp-0.1.0/DEPLOY.md +207 -0
  4. vayl_mcp-0.1.0/LICENSE +201 -0
  5. vayl_mcp-0.1.0/MANIFEST.in +4 -0
  6. vayl_mcp-0.1.0/NOTICE +40 -0
  7. vayl_mcp-0.1.0/PKG-INFO +542 -0
  8. vayl_mcp-0.1.0/README.md +503 -0
  9. vayl_mcp-0.1.0/SECURITY.md +187 -0
  10. vayl_mcp-0.1.0/benchmarks/__init__.py +1 -0
  11. vayl_mcp-0.1.0/benchmarks/clinical/__init__.py +1 -0
  12. vayl_mcp-0.1.0/benchmarks/clinical/patients.py +421 -0
  13. vayl_mcp-0.1.0/benchmarks/clinical/run.py +274 -0
  14. vayl_mcp-0.1.0/benchmarks/common/__init__.py +1 -0
  15. vayl_mcp-0.1.0/benchmarks/common/llm_client.py +108 -0
  16. vayl_mcp-0.1.0/benchmarks/common/metrics.py +138 -0
  17. vayl_mcp-0.1.0/benchmarks/common/schema.py +88 -0
  18. vayl_mcp-0.1.0/benchmarks/common/vayl_client.py +260 -0
  19. vayl_mcp-0.1.0/benchmarks/evaluations/compare_systems.py +317 -0
  20. vayl_mcp-0.1.0/benchmarks/evaluations/eval_adversarial.py +181 -0
  21. vayl_mcp-0.1.0/benchmarks/evaluations/eval_graph.py +114 -0
  22. vayl_mcp-0.1.0/benchmarks/evaluations/eval_reconcile.py +119 -0
  23. vayl_mcp-0.1.0/benchmarks/evaluations/graph_headtohead.py +226 -0
  24. vayl_mcp-0.1.0/benchmarks/evaluations/messy_eval.py +136 -0
  25. vayl_mcp-0.1.0/benchmarks/evaluations/rep_eval.py +51 -0
  26. vayl_mcp-0.1.0/benchmarks/evaluations/retraction_battery.py +268 -0
  27. vayl_mcp-0.1.0/benchmarks/evaluations/scale_bench.py +297 -0
  28. vayl_mcp-0.1.0/benchmarks/load/__init__.py +1 -0
  29. vayl_mcp-0.1.0/benchmarks/load/concurrency.py +263 -0
  30. vayl_mcp-0.1.0/benchmarks/load/integrity.py +210 -0
  31. vayl_mcp-0.1.0/benchmarks/locomo/__init__.py +1 -0
  32. vayl_mcp-0.1.0/benchmarks/locomo/prompts.py +283 -0
  33. vayl_mcp-0.1.0/benchmarks/locomo/run.py +575 -0
  34. vayl_mcp-0.1.0/benchmarks/scripts/run_eval_gpt4omini.sh +37 -0
  35. vayl_mcp-0.1.0/benchmarks/stress/stress_test.py +90 -0
  36. vayl_mcp-0.1.0/docs/README.md +12 -0
  37. vayl_mcp-0.1.0/pyproject.toml +79 -0
  38. vayl_mcp-0.1.0/setup.cfg +4 -0
  39. vayl_mcp-0.1.0/src/vayl/__init__.py +2 -0
  40. vayl_mcp-0.1.0/src/vayl/api/__init__.py +0 -0
  41. vayl_mcp-0.1.0/src/vayl/api/mcp_server.py +954 -0
  42. vayl_mcp-0.1.0/src/vayl/api/server.py +267 -0
  43. vayl_mcp-0.1.0/src/vayl/auth/__init__.py +0 -0
  44. vayl_mcp-0.1.0/src/vayl/auth/auth.py +206 -0
  45. vayl_mcp-0.1.0/src/vayl/auth/sso.py +133 -0
  46. vayl_mcp-0.1.0/src/vayl/clinical/__init__.py +1 -0
  47. vayl_mcp-0.1.0/src/vayl/clinical/fhir.py +167 -0
  48. vayl_mcp-0.1.0/src/vayl/clinical/medrec.py +186 -0
  49. vayl_mcp-0.1.0/src/vayl/licensing/__init__.py +0 -0
  50. vayl_mcp-0.1.0/src/vayl/licensing/license.py +136 -0
  51. vayl_mcp-0.1.0/src/vayl/licensing/mint_license.py +79 -0
  52. vayl_mcp-0.1.0/src/vayl/licensing/receipts.py +130 -0
  53. vayl_mcp-0.1.0/src/vayl/memory/__init__.py +0 -0
  54. vayl_mcp-0.1.0/src/vayl/memory/decisions.py +162 -0
  55. vayl_mcp-0.1.0/src/vayl/memory/llm_memory.py +1289 -0
  56. vayl_mcp-0.1.0/src/vayl/memory/orgmemory.py +59 -0
  57. vayl_mcp-0.1.0/src/vayl/memory/reconcile.py +424 -0
  58. vayl_mcp-0.1.0/src/vayl/memory/schema.py +189 -0
  59. vayl_mcp-0.1.0/src/vayl/security/__init__.py +0 -0
  60. vayl_mcp-0.1.0/src/vayl/security/audit.py +203 -0
  61. vayl_mcp-0.1.0/src/vayl/security/crypto.py +171 -0
  62. vayl_mcp-0.1.0/src/vayl/security/kms.py +88 -0
  63. vayl_mcp-0.1.0/src/vayl/security/safety.py +54 -0
  64. vayl_mcp-0.1.0/src/vayl/storage/__init__.py +0 -0
  65. vayl_mcp-0.1.0/src/vayl/storage/db.py +204 -0
  66. vayl_mcp-0.1.0/src/vayl/storage/graph_store.py +232 -0
  67. vayl_mcp-0.1.0/src/vayl/storage/store.py +407 -0
  68. vayl_mcp-0.1.0/src/vayl/telemetry/__init__.py +0 -0
  69. vayl_mcp-0.1.0/src/vayl/telemetry/metrics.py +92 -0
  70. vayl_mcp-0.1.0/src/vayl_mcp.egg-info/PKG-INFO +542 -0
  71. vayl_mcp-0.1.0/src/vayl_mcp.egg-info/SOURCES.txt +94 -0
  72. vayl_mcp-0.1.0/src/vayl_mcp.egg-info/dependency_links.txt +1 -0
  73. vayl_mcp-0.1.0/src/vayl_mcp.egg-info/entry_points.txt +4 -0
  74. vayl_mcp-0.1.0/src/vayl_mcp.egg-info/requires.txt +21 -0
  75. vayl_mcp-0.1.0/src/vayl_mcp.egg-info/top_level.txt +1 -0
  76. vayl_mcp-0.1.0/tests/test_accountability.py +541 -0
  77. vayl_mcp-0.1.0/tests/test_api.py +245 -0
  78. vayl_mcp-0.1.0/tests/test_apply.py +733 -0
  79. vayl_mcp-0.1.0/tests/test_auth.py +440 -0
  80. vayl_mcp-0.1.0/tests/test_clinical.py +396 -0
  81. vayl_mcp-0.1.0/tests/test_confirmation.py +191 -0
  82. vayl_mcp-0.1.0/tests/test_db.py +66 -0
  83. vayl_mcp-0.1.0/tests/test_events.py +320 -0
  84. vayl_mcp-0.1.0/tests/test_graph_store.py +168 -0
  85. vayl_mcp-0.1.0/tests/test_http_pool.py +83 -0
  86. vayl_mcp-0.1.0/tests/test_licensing.py +173 -0
  87. vayl_mcp-0.1.0/tests/test_locomo_harness.py +251 -0
  88. vayl_mcp-0.1.0/tests/test_metrics.py +81 -0
  89. vayl_mcp-0.1.0/tests/test_orgmemory.py +159 -0
  90. vayl_mcp-0.1.0/tests/test_postgres.py +152 -0
  91. vayl_mcp-0.1.0/tests/test_recall.py +327 -0
  92. vayl_mcp-0.1.0/tests/test_reconcile_engine.py +93 -0
  93. vayl_mcp-0.1.0/tests/test_safety.py +130 -0
  94. vayl_mcp-0.1.0/tests/test_security.py +353 -0
  95. vayl_mcp-0.1.0/tests/test_slots.py +448 -0
  96. vayl_mcp-0.1.0/tests/test_store.py +491 -0
@@ -0,0 +1,222 @@
1
+ # Vayl — EU Compliance Deployment Guide
2
+
3
+ For a Data Protection Officer / privacy counsel evaluating Vayl for deployment that processes
4
+ **EU residents' personal data**, in the **on-premise / self-hosted** model (your organization runs
5
+ Vayl inside your own environment).
6
+
7
+ > **This is not legal advice.** It is a technical mapping of Vayl's features to the obligations you
8
+ > are likely to face, and an honest split of who is responsible for what. Your DPO / counsel makes
9
+ > the final determination for your specific processing. Vayl provides *building blocks*; it does not,
10
+ > and cannot, make your organization compliant.
11
+
12
+ ## Roles (this is the foundation)
13
+
14
+ In the on-prem model:
15
+
16
+ | Party | Role under GDPR | Consequence |
17
+ |---|---|---|
18
+ | **Your organization** (the deployer) | **Data controller** | You carry the obligations below. |
19
+ | **Vayl** (the software vendor) | **Not a processor** — it runs on your infrastructure and the vendor never receives your data | No controller↔vendor DPA needed for the local deployment. |
20
+ | **Your chosen LLM/embedding provider** *(only if cloud)* | **Sub-processor** | You need a DPA + a transfer basis with them. **Avoided entirely if you use the local model** (Vayl's default). |
21
+
22
+ ## The two decisions that dominate your compliance
23
+
24
+ 1. **LLM choice = your international-transfer position.**
25
+ - **Local model (Vayl's default): personal data never leaves the machine.** No sub-processor, no
26
+ Art. 44 transfer, strong data residency. This is the compliant-by-default configuration.
27
+ - **Cloud model (e.g. US-hosted): memory text is sent to that provider.** That provider becomes a
28
+ sub-processor; you need a DPA and a **Chapter V transfer basis** (adequacy, EU-US Data Privacy
29
+ Framework certification, or SCCs + a Transfer Impact Assessment). For EU data, prefer a local or
30
+ EU-hosted model.
31
+ 2. **EU AI Act risk class = your *use case*, not Vayl.** A memory layer isn't classified on its own;
32
+ the *system it's part of* is. See "EU AI Act" below.
33
+
34
+ ## GDPR — obligations and the responsibility split
35
+
36
+ | GDPR requirement | Owner | What Vayl provides |
37
+ |---|---|---|
38
+ | **Lawful basis** (Art. 6) — consent / legitimate interest / contract | **You** | — |
39
+ | **Transparency / privacy notice** (Art. 13–14) | **You** | — |
40
+ | **Data-subject access** (Art. 15) | **You** run the process | `list_memories`, `get_memory`, `history` |
41
+ | **Rectification** (Art. 16) | **You** | `update_memory` (audit-preserving) |
42
+ | **Erasure / right to be forgotten** (Art. 17) | **You** | `delete(subject)` / `delete_all()` — **hard delete**, history and graph included |
43
+ | **Restriction / objection** (Art. 18, 21) | **You** | scope isolation via `user_id`/`agent_id`/`run_id` |
44
+ | **Portability / access** (Art. 20, 15) | **You** | ✅ `export_memory` — machine-readable JSON: statements (active + history) **plus decision snapshots, audit entries, and receipts** about the subject |
45
+ | **Accountability** (Art. 5(2)) | **Shared** | ✅ append-only `audit_log` — who / what / when, for every data operation |
46
+ | **Storage limitation** (Art. 5(1)(e)) | **You** set the policy | ✅ `purge_expired` — hard-delete records older than N days; flags extend it to audit / decisions / receipts (audit chain stays verifiable via a signed anchor) |
47
+ | **Security of processing** (Art. 32) | **Shared** | **encryption at rest** (on by default), auth, localhost-only, no telemetry, parameterized queries |
48
+ | **Records of Processing** (Art. 30) | **You** | data-flow description (this doc + `SECURITY.md`) |
49
+ | **DPIA** (Art. 35) — likely required for AI profiling / large-scale | **You** | inputs: architecture, data-flow, security controls, this doc |
50
+ | **Processor agreement** (Art. 28) | **You**, *only if* using a cloud LLM | local default avoids it |
51
+ | **Breach notification** (Art. 33–34, 72h) | **You** | — |
52
+ | **DPO** (Art. 37), if required | **You** | — |
53
+ | **Privacy by design & default** (Art. 25) | **Shared** | local-first, erasure built in, encrypted, minimal egress |
54
+
55
+ ### Storage limitation & retention (Art. 5(1)(e)) — read this carefully
56
+ Vayl is **event-sourced**: `forget` **retracts but retains history** (auditable), while
57
+ `delete`/`delete_all` **hard-erase** everything. These are different tools for different obligations:
58
+ - Map a **retention/minimization** policy to periodic `delete` of stale subjects.
59
+ - Map a **data-subject erasure request** to **`delete`/`delete_all`**, *not* `forget` — `forget`
60
+ keeps the value in history, which does **not** satisfy Art. 17.
61
+
62
+ Vayl provides `purge_expired(older_than_days)` to enforce a retention window (and `audit_log` records
63
+ every erasure as evidence). You still set and document the *policy*; schedule the purge accordingly.
64
+
65
+ ### Erasure vs. accountability — a tension to resolve explicitly
66
+ `delete`/`delete_all` hard-erase the **facts**, but the **audit log and erasure receipts deliberately
67
+ retain an (encrypted) reference to the subject** — that retention *is* the accountability feature
68
+ (Art. 5(2)), and it is in tension with erasure (Art. 17). Decide and document one of:
69
+ - **Encrypted retention** — the audit/receipt detail is Fernet ciphertext at rest (pseudonymized),
70
+ retained under Art. 17(3)(b)/(e) (legal obligation / legal claims) for a defined period; **or**
71
+ - **Crypto-shredding** — destroy the encryption key to render the retained references unrecoverable
72
+ (effective erasure of the audit detail too); **or**
73
+ - **Redaction** — purge/anonymize the audit entries for that subject.
74
+
75
+ The signed **erasure receipt** proves the facts were deleted; the retained (encrypted) audit reference
76
+ is your accountability trail — choose its fate per your retention policy, don't leave it implicit.
77
+
78
+ **Decision snapshots are redacted on erasure.** Decision records snapshot the beliefs behind an
79
+ action — including the personal values. `delete`/`delete_all` therefore also **redact the erased
80
+ values from those snapshots**: the decision rows and summaries remain (the accountability record of
81
+ *what* was decided), the value is replaced with an explicit redaction marker, the decision is
82
+ **re-signed** so verification still passes, and the redaction itself is recorded in the audit log.
83
+ Retention for the append-only tables exists too: `purge_expired(include_audit / include_decisions /
84
+ include_receipts)` — the audit chain stays verifiable across purges via a **signed retention anchor**.
85
+
86
+ ### Special-category data (Art. 9)
87
+ If the agent may store health, biometric, racial, political, religious, sexual-orientation, or
88
+ trade-union data, that is special-category: you generally need **explicit consent** (or another Art. 9
89
+ condition) and heightened safeguards. Vayl's encryption-at-rest helps with Art. 32 but does **not**
90
+ provide the legal basis.
91
+
92
+ ### Automated decision-making (Art. 22)
93
+ If Vayl's memory feeds **automated decisions with legal or similarly significant effects**, data
94
+ subjects have rights to human intervention and to contest. Vayl gives you concrete controls for this
95
+ (see "Trust-layer controls" below): the **safety gate** (`check_before_act` / `safe_recall`) is a
96
+ testable human-in-the-loop trigger — it refuses to act on disputed/low-confidence/stale memory and
97
+ routes to a human — while **belief provenance** and **`explain_decision`** supply the "meaningful
98
+ information about the logic" a data subject can be given, and the basis on which they contest.
99
+
100
+ ## EU AI Act
101
+
102
+ Vayl is a component of an AI system. Your obligations depend on how you use it:
103
+
104
+ - **Roles:** you are typically the **deployer** (and possibly **provider** if you build the agent);
105
+ the foundation-model vendor is the **GPAI provider**. Obligations differ per role.
106
+ - **Risk classification (your use case decides):**
107
+ - **Prohibited** practices (e.g. social scoring, certain biometric categorization) — do not deploy.
108
+ - **High-risk** (Annex III: employment, credit, education, essential services, biometric,
109
+ law-enforcement, migration): triggers conformity assessment, risk management, logging, human
110
+ oversight, technical documentation. If your agent operates here, the **system** is high-risk.
111
+ Several of these duties map to Vayl's trust layer — **logging/traceability (Art. 12)** → the
112
+ tamper-evident audit + decision records; **human oversight (Art. 14)** → the safety gate;
113
+ **transparency (Art. 13)** → provenance/`explain_decision`; **accuracy (Art. 15)** →
114
+ reconciliation. See "Trust-layer controls" below. These *support* the obligations; they don't
115
+ discharge them.
116
+ - **Limited-risk** (a chatbot / assistant interacting with people): **transparency** — you must
117
+ inform users they are interacting with AI, and label AI-generated content where applicable.
118
+ - **Minimal-risk:** most back-office memory use — no specific AI Act obligations.
119
+ - **GPAI obligations** sit mainly with the **model provider**. Using a **local open model** reduces
120
+ your exposure to third-party GPAI dependencies.
121
+
122
+ The AI Act applies in phases; confirm the current applicable dates and your classification with counsel.
123
+
124
+ ## Trust-layer controls — provenance, safety gate, verifiable memory
125
+
126
+ Beyond the data-subject-rights tooling above, Vayl implements a *trust layer* that maps directly to
127
+ GDPR accountability/accuracy and to EU AI Act high-risk duties. These are the controls to put in front
128
+ of a DPO evaluating **agent autonomy** — the "can we let it act, and can we prove why it did" question.
129
+
130
+ | Feature | What it does | Serves |
131
+ |---|---|---|
132
+ | **Belief provenance** (`recall(explain=True)`) | Returns the exact facts behind an answer — value, **who asserted it** (source), confidence, what it superseded | GDPR Art. 15/22 (meaningful info about the logic); AI Act Art. 13 (transparency) |
133
+ | **Decision audit** (`record_decision` / `explain_decision`) | Immutable, **signed** snapshot of the beliefs behind an agent action — answers "why did it do X?" even after the facts change | GDPR Art. 5(2) accountability, Art. 22 (contest a decision); AI Act Art. 12 (record-keeping) |
134
+ | **Safety gate** (`check_before_act` / `safe_recall`) | Refuses to *act* on disputed / low-confidence / stale / superseded memory; surfaces it for a human | GDPR Art. 22 (human intervention); AI Act Art. 14 (human oversight) |
135
+ | **Tamper-evident audit** (`verify_audit`) | Hash-chained + Ed25519-signed audit log; proves it wasn't edited, reordered, or truncated | GDPR Art. 5(1)(f) integrity, Art. 5(2); AI Act Art. 12 |
136
+ | **Signed erasure receipts** (`delete` → `verify_receipt`) | A third-party-verifiable proof that an erasure happened | GDPR Art. 17 (demonstrate erasure) |
137
+ | **Knowledge attestation** (`attest`) | Signed proof of what was held at a point in time | Accountability / evidentiary |
138
+ | **Source attribution** (`remember(source=…)`) | Records which agent/person/connector asserted each fact; drives source-aware reconciliation in shared spaces | Accuracy (5(1)(d)), accountability, data provenance |
139
+ | **Public-key verification** (`export_public_key`) | Anyone verifies receipts, attestations, and the audit chain **without the secret key or DB access** | Independent auditability |
140
+
141
+ **Human oversight (Art. 22 / AI Act Art. 14) — how to wire it.** Put `check_before_act` (or
142
+ `safe_recall`) on the path to any autonomous action with legal or significant effect. When it returns
143
+ BLOCKED/WITHHELD, route to a human. That is a concrete, testable oversight control — not a policy
144
+ promise — and provenance + `explain_decision` give the reviewer the "why" they need to act or contest.
145
+
146
+ **Key management (Art. 32).** The encryption (Fernet) and signing (Ed25519) keys underpin these
147
+ guarantees. Vayl supports **HashiCorp Vault Transit** envelope encryption out of the box
148
+ (`VAYL_KMS=vault`): the master key never leaves Vault, only a *wrapped* key blob sits on the Vayl host,
149
+ and the key is unwrapped into memory at startup — so custody moves **off the data host** (a key beside
150
+ the DB protects against file copy, not machine theft). Self-hosted Vault keeps that custody on your own
151
+ infrastructure, consistent with the sovereignty posture. Vayl **fails closed** if the KMS is unreachable
152
+ (refuses to start rather than run unencrypted). The tamper-evidence and receipts are only as strong as
153
+ the custody of the signing key — treat Vault access, and key rotation, accordingly.
154
+
155
+ ## Sector overlays (if applicable)
156
+ - **Finance:** DORA (operational resilience, ICT risk).
157
+ - **Essential / important entities:** NIS2 (security measures, incident reporting).
158
+ - **Health:** national health-data law and the European Health Data Space.
159
+
160
+ These govern your environment, not Vayl specifically.
161
+
162
+ ## Why Vayl fits an EU deployment
163
+ - **Data residency:** local/on-prem + **local-LLM default → personal data stays on the machine.**
164
+ - **Security:** **encrypted at rest** (Art. 32), authenticated, localhost-only, no telemetry.
165
+ - **Data-subject rights, built in:** erasure, access, rectification map to concrete tools.
166
+ - **Auditability:** the history/change-log evidences what was held and when it changed.
167
+
168
+ This equips you to comply; it does not replace your legal basis, notices, DPIA, or DPO sign-off.
169
+
170
+ ## Evidence of implemented controls — verify each yourself
171
+
172
+ Vayl does **not** self-declare compliance (that is your deployment's property, validated by your
173
+ auditor). What it *can* prove is the **technical controls it implements** — and every one below is
174
+ **independently verifiable** by the referenced command or automated test. This is auditor-grade
175
+ evidence, not a claim. Run the full suite with `pytest` (151 automated tests).
176
+
177
+ | Control (requirement) | What Vayl does | How to prove it |
178
+ |---|---|---|
179
+ | **Encryption at rest** (Art. 32) | Content columns are Fernet ciphertext on disk | Store a fact, then `strings vayl.db \| grep <the value>` → **nothing**; the raw `value` column is `gAAAAA…`. Test: `test_crypto.py::test_data_is_ciphertext_at_rest_but_plaintext_on_read` |
180
+ | **Right to erasure** (Art. 17) | `delete` / `delete_all` hard-delete rows **incl. history and graph** | After erasure, `export_memory` returns `count: 0` and `history` is empty; `audit_log` shows a `delete(erasure)` entry. Test: `test_store.py::test_delete_hard_erases_a_subject_including_history` |
181
+ | **Access & portability** (Art. 15/20) | `export_memory` → machine-readable JSON (statements + decisions + audit + receipts) | Call `export_memory`; output is valid JSON with every record. Test: `test_compliance.py::test_export_is_machine_readable_and_includes_history` |
182
+ | **Rectification** (Art. 16) | `update_memory` retires the old value to history, sets the new one active | Test: `test_apply.py::test_update_is_audit_preserving` |
183
+ | **Accountability** (Art. 5(2)) | Append-only `audit_log` records who/what/when for every data op | Do any operation, then `audit_log` shows the timestamped entry. Test: `test_compliance.py::test_audit_records_who_what_when_newest_first` |
184
+ | **Storage limitation** (Art. 5(1)(e)) | `purge_expired(days)` hard-deletes rows older than N days | Test: `test_compliance.py::test_expire_deletes_only_old_rows` |
185
+ | **Data residency / no egress** | Defaults to a local LLM — no data leaves the host | Boot with no LLM env → it uses local Ollama. Test: `test_config.py::test_out_of_the_box_is_local_ollama_no_egress` |
186
+ | **Access control** | `vayl-server` requires a Bearer credential on every request; roles grant capabilities (RBAC), fail-closed | Request without / with an invalid key → `401`; a valid key binds its principal. Tests: `test_api.py::test_missing_key_is_401`, `test_invalid_or_malformed_key_is_401`, `test_valid_key_binds_the_principal_for_the_request` |
187
+ | **Space isolation** (data minimization) | `(user_id, agent_id, run_id)` scoping | Test: `test_store.py::test_agent_spaces_are_isolated_for_the_same_user` |
188
+ | **No telemetry** | Only outbound call is the LLM/embedder you configure | Code review — grep for network calls; there are no analytics/telemetry endpoints |
189
+ | **Injection resistance** (Art. 32) | All SQL parameterized | Code review of `store.py`; stress-tested with injection strings |
190
+ | **Tamper-evident audit** (Art. 5(1)(f), 5(2)) | Audit log is hash-chained + Ed25519-signed | `verify_audit` reports INTACT; edit any row → it names the broken seq. Tests: `test_audit.py::test_tampering_a_detail_breaks_the_chain`, `::test_signed_chain_verifies_and_signature_tamper_is_caught` |
191
+ | **Proof of erasure** (Art. 17) | `delete` issues a signed, third-party-verifiable receipt | `verify_receipt` → VALID; edit any field → INVALID. Tests: `test_receipts.py::test_erasure_receipt_verifies_with_public_key_only`, `::test_editing_any_payload_field_invalidates_the_receipt` |
192
+ | **Decision accountability** (Art. 5(2), 22) | Immutable **signed** snapshot of the beliefs behind an action | Snapshot survives later fact changes; receipt verifies. Tests: `test_decisions.py::test_snapshot_is_immutable_across_later_fact_changes`, `::test_signed_receipt_verifies_and_tamper_is_caught` |
193
+ | **Human-oversight hook** (Art. 22 / AI Act 14) | Safety gate refuses to act on unsafe memory | `check_before_act`/`safe_recall` BLOCK on flagged/low-conf/stale. Tests: `test_safety.py::test_check_blocks_a_flagged_conflict`, `::test_safe_recall_withholds_on_low_confidence` |
194
+ | **Belief provenance** (Art. 15/22, AI Act 13) | Recall returns the exact facts + source used to answer | Test: `test_apply.py::test_query_with_provenance_returns_exact_facts_used` |
195
+ | **Independent verifiability** | Public key verifies receipts/attestations/chain without the secret | `export_public_key`, then `receipts.verify(receipt, public_key)` off-box → True. Test: `test_receipts.py::test_persisted_receipt_roundtrips_and_stays_verifiable` |
196
+
197
+ **What this proves and what it doesn't.** It proves Vayl *implements* these controls, verifiably.
198
+ It does **not** prove your *deployment* is compliant — that additionally requires your legal basis,
199
+ notices, DPIA, RoPA, retention decisions, AI-Act classification, and your auditor's/DPO's sign-off
200
+ (and, for a certification, an accredited assessor). Vayl provides the evidence; you and your auditor
201
+ provide the compliance.
202
+
203
+ ## DPO / controller checklist
204
+
205
+ - [ ] Establish and document a **lawful basis** for the processing.
206
+ - [ ] Publish a **privacy notice** covering the memory processing.
207
+ - [ ] Complete a **DPIA** (AI + personal-data processing is typically in scope).
208
+ - [ ] Maintain **Records of Processing** (Art. 30).
209
+ - [ ] Choose the **local/EU model** for data residency; if cloud, put a **DPA + transfer basis** in place.
210
+ - [ ] Define a **retention schedule**; map erasure requests to `delete`/`delete_all` (not `forget`).
211
+ - [ ] Implement the **data-subject-rights workflow** using Vayl's tools.
212
+ - [ ] Confirm **special-category** handling (Art. 9) and **Art. 22** human oversight if relevant.
213
+ - [ ] Put the **safety gate** (`check_before_act` / `safe_recall`) on autonomous-action paths and route BLOCKED/WITHHELD to a human (Art. 22 / AI Act Art. 14).
214
+ - [ ] Decide the **audit/receipt retention vs. crypto-shred** policy for erased subjects (see "Erasure vs. accountability").
215
+ - [ ] Use **`VAYL_KMS=vault`** (HashiCorp Vault Transit) for production key custody; enable rotation, keep the master key off the data host.
216
+ - [ ] Classify the system under the **AI Act** and meet the applicable transparency/high-risk duties.
217
+ - [ ] Keep OS **full-disk encryption** and access controls on the host (see `SECURITY.md`).
218
+ - [ ] Have counsel/DPO **sign off** before go-live; commission a **security/pen test**.
219
+
220
+ ## Disclaimer
221
+ Vayl is early-stage software, is not certified, and makes no compliance guarantee. This guide is a
222
+ technical aid for your assessment, not a legal opinion. Determinations rest with your DPO and counsel.
@@ -0,0 +1,38 @@
1
+ # Contributing to Vayl
2
+
3
+ Thanks for your interest in contributing! Bug reports, docs, tests, new LLM providers, and
4
+ reconciliation edge cases are especially welcome.
5
+
6
+ The full contributor guide — setup, the free-threaded workflow, benchmarks, and the project layout —
7
+ lives in the README:
8
+
9
+ 👉 **[README → Contributing](README.md#contributing)**
10
+
11
+ ## TL;DR
12
+
13
+ ```bash
14
+ git clone https://github.com/vayl-dev/vayl && cd vayl
15
+ python -m venv .venv && source .venv/bin/activate
16
+ pip install -e ".[dev,server,postgres]"
17
+ pytest # 475 offline unit tests (deterministic, no LLM/network)
18
+ ruff check . # lint
19
+ ```
20
+
21
+ ## Ground rules
22
+
23
+ - Keep the **core at two dependencies** (`mcp`, `cryptography`); anything heavier goes behind an
24
+ optional extra in `pyproject.toml`.
25
+ - Unit tests stay **offline and deterministic** — LLM-dependent checks belong in `benchmarks/`.
26
+ - New behaviour needs a test, and `ruff check .` must pass.
27
+ - The audit hash-chain is a security guarantee: changes under `src/vayl/security/audit.py` need a
28
+ concurrency test (see `tests/test_accountability.py`).
29
+ - For anything substantial, **open an issue first** so we can align on approach before a big PR.
30
+
31
+ By contributing, you agree your contributions are licensed under **Apache-2.0**.
32
+
33
+ ## Reporting issues
34
+
35
+ - **Bugs & feature requests:** open a [GitHub issue](https://github.com/vayl-dev/vayl/issues).
36
+ - **Security vulnerabilities:** please do **not** open a public issue — contact the maintainer
37
+ privately instead. Vayl's threat model and shared-responsibility matrix are in
38
+ [`SECURITY.md`](SECURITY.md).
@@ -0,0 +1,207 @@
1
+ # Deploying Vayl (self-hosted)
2
+
3
+ A step-by-step runbook to stand up Vayl in your own environment. Aimed at an ops/platform engineer;
4
+ follow it top to bottom. End state: an authenticated Vayl memory server your agents connect to over
5
+ HTTPS, running entirely on your infrastructure.
6
+
7
+ > Vayl is self-hosted: your data (and, with Vault, your keys) never leave your environment. The one
8
+ > external dependency is the **LLM** — and even that can be a model you host (Ollama / vLLM / an
9
+ > EU-hosted endpoint), so there's no egress at all if you want none.
10
+
11
+ ---
12
+
13
+ ## 0. Prerequisites
14
+
15
+ - **Docker** + **Docker Compose** on a Linux host (2 vCPU / 4 GB RAM is enough if you use a *hosted*
16
+ LLM; running a local model needs much more — see the LLM note below).
17
+ - **An LLM endpoint** — either your own OpenAI-compatible API (OpenAI, Azure OpenAI, an EU-hosted
18
+ provider, vLLM), or Ollama running on the host. Vayl needs a chat model **and** an embedding model.
19
+ - A **DNS name + TLS cert** for production (any reverse proxy works; a Caddy example is below).
20
+ - (Optional) Postgres, HashiCorp Vault, an OIDC IdP, an enterprise license — all covered as options.
21
+
22
+ ---
23
+
24
+ ## 1. Get the files
25
+
26
+ **If your vendor gave you an image** (the usual case): put
27
+ `docker-compose.client.yml`, `.env.example`, and this file in a directory, then log in to the registry
28
+ with the pull credentials you were given:
29
+
30
+ ```bash
31
+ docker login ghcr.io # (or your vendor's registry) with the credentials provided
32
+ cp .env.example .env
33
+ ```
34
+ Throughout this runbook, use `docker compose -f docker-compose.client.yml …` for compose commands.
35
+
36
+ **If you're building from source** (vendor/internal): clone the repo and use plain `docker compose`:
37
+
38
+ ```bash
39
+ git clone <vayl-repo> vayl && cd vayl
40
+ cp .env.example .env
41
+ ```
42
+
43
+ ## 2. Configure — edit `.env`
44
+
45
+ Open `.env`. **The LLM block is the only required part.** Pick one:
46
+
47
+ **Path A — your existing LLM (recommended for production):**
48
+ ```env
49
+ LLM_PROVIDER=openai
50
+ OPENAI_BASE_URL=https://your-endpoint/v1 # OpenAI, Azure OpenAI, EU-hosted, vLLM…
51
+ OPENAI_API_KEY=sk-...
52
+ OPENAI_MODEL=gpt-5-mini # 0% silently-wrong on messy data; gpt-5-nano is cheaper but weaker on corrections
53
+ EMBED_BASE_URL=https://your-endpoint/v1
54
+ EMBED_API_KEY=sk-...
55
+ EMBED_MODEL=text-embedding-3-small
56
+ ```
57
+
58
+ **Path B — Ollama on the host (no egress; needs RAM/GPU; weaker on small models):**
59
+ ```bash
60
+ # on the Docker host:
61
+ ollama serve & # if not already running
62
+ ollama pull qwen2.5:3b # chat model ollama pull nomic-embed-text # embeddings
63
+ ```
64
+ ```env
65
+ LLM_PROVIDER=openai
66
+ OPENAI_BASE_URL=http://host.docker.internal:11434/v1
67
+ OPENAI_API_KEY=ollama
68
+ OPENAI_MODEL=qwen2.5:3b
69
+ EMBED_BASE_URL=http://host.docker.internal:11434/v1
70
+ EMBED_API_KEY=ollama
71
+ EMBED_MODEL=nomic-embed-text
72
+ ```
73
+ > **LLM quality matters.** Vayl's reconciliation is only as good as the model. A 3B local model is fine
74
+ > for a demo but weak on hard cases; use a strong hosted model (or a larger local one) for production.
75
+
76
+ Encryption (`VAYL_ENCRYPT=on`) and auth (`VAYL_AUTH_REQUIRED=1`) are on by default — leave them.
77
+
78
+ ## 3. Start
79
+
80
+ ```bash
81
+ docker compose up -d --build
82
+ docker compose logs -f vayl # watch for "Uvicorn running" / "Application startup complete"
83
+ ```
84
+
85
+ ## 4. Bootstrap the first admin (one-off)
86
+
87
+ The server requires authenticated principals. Mint the first admin and **copy the key — it's shown once**:
88
+
89
+ ```bash
90
+ docker compose run --rm vayl python -c "import mcp_server as s; print(s.create_principal('admin', role='admin'))"
91
+ ```
92
+
93
+ Use that admin key to create the day-to-day principals your agents/users will use (roles:
94
+ `admin` / `member` / `agent` / `viewer` / `auditor`) — via the `create_principal` tool, or repeat the
95
+ command above with a different name/role.
96
+
97
+ ## 5. Verify
98
+
99
+ ```bash
100
+ curl -s localhost:8080/healthz # {"status":"ok"}
101
+ curl -s localhost:8080/readyz # {"status":"ready"} (DB reachable)
102
+ curl -s localhost:8080/metrics # Prometheus metrics
103
+
104
+ # a real call requires the admin key and the MCP protocol; quickest smoke — auth is enforced:
105
+ curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/mcp # → 401 (no key = rejected)
106
+ ```
107
+ `health` (via the tool) also checks the LLM + embedder are reachable — run it once you've connected a
108
+ client (below). If it reports `llm: FAIL`, your LLM env in `.env` is wrong.
109
+
110
+ ## 6. Put TLS in front (production)
111
+
112
+ Do **not** expose `:8080` directly. Terminate TLS at a reverse proxy. Simplest — add Caddy to the stack:
113
+
114
+ `Caddyfile`:
115
+ ```
116
+ vayl.your-domain.com {
117
+ reverse_proxy vayl:8080
118
+ }
119
+ ```
120
+ Add to `docker-compose.yml`:
121
+ ```yaml
122
+ caddy:
123
+ image: caddy:2
124
+ ports: ["443:443", "80:80"]
125
+ volumes: ["./Caddyfile:/etc/caddy/Caddyfile", "caddy-data:/data"]
126
+ depends_on: [vayl]
127
+ # and add `caddy-data:` under top-level volumes
128
+ ```
129
+ (nginx/Traefik/your ingress work the same — proxy `https://vayl.your-domain.com` → `vayl:8080`.)
130
+
131
+ ## 7. Connect an agent / MCP client
132
+
133
+ Vayl speaks **MCP over streamable-HTTP** at `/mcp`, Bearer-authenticated. Point any MCP client
134
+ (Claude Desktop, Cursor, your agent framework) at it:
135
+
136
+ ```
137
+ URL: https://vayl.your-domain.com/mcp
138
+ Header: Authorization: Bearer vayl_sk_<the principal's key>
139
+ ```
140
+
141
+ The client then has the memory tools (`remember`, `recall`, `check_before_act`, …) gated by that
142
+ principal's role.
143
+
144
+ ---
145
+
146
+ ## Options (skip any you don't need)
147
+
148
+ **Postgres (scale-out / multiple server processes):**
149
+ ```bash
150
+ # in .env: VAYL_DATABASE_URL=postgresql://vayl:vayl@postgres:5432/vayl (change the password!)
151
+ docker compose --profile postgres up -d
152
+ ```
153
+ Same-space writes serialize across processes via an advisory lock; different spaces run in parallel.
154
+
155
+ **HashiCorp Vault (key custody off the host):**
156
+ ```bash
157
+ # one-time in Vault: vault secrets enable transit && vault write -f transit/keys/vayl
158
+ # in .env:
159
+ VAYL_KMS=vault
160
+ VAULT_ADDR=https://vault.internal:8200
161
+ VAULT_TOKEN=<a token with transit encrypt/decrypt on the vayl key>
162
+ ```
163
+ The master key never leaves Vault; only a wrapped blob sits on the host. If Vault is unreachable, Vayl
164
+ **fails closed** (won't start) rather than run unencrypted.
165
+
166
+ **Enterprise license (raise seat cap / unlock features):** set `VAYL_VENDOR_PUBKEY` + `VAYL_LICENSE`
167
+ in `.env` (from your vendor). Check with the `license_status` tool. Omit → Community edition.
168
+
169
+ **SSO / OIDC (human login via your IdP; requires a license):** set `VAYL_OIDC_ISSUER`,
170
+ `VAYL_OIDC_AUDIENCE`, `VAYL_OIDC_JWKS_URL`. Users then present an IdP JWT as the Bearer token; API keys
171
+ still work alongside.
172
+
173
+ **Graph recall (Neo4j):** `docker compose --profile graph up -d`, set `VAYL_GRAPH=1` + `NEO4J_*`.
174
+
175
+ ---
176
+
177
+ ## Operations
178
+
179
+ - **Backups:** back up the `vayl-data` volume (SQLite DB + keys) — or your Postgres, if used. The
180
+ `<db>.key*` files are the *only* way to decrypt at-rest data; back them up securely (or use Vault,
181
+ where the master key is in Vault and only a wrapped blob is on the host).
182
+ - **Upgrades:** `git pull && docker compose up -d --build`. Schema migrations are automatic and
183
+ backward-compatible.
184
+ - **Logs & metrics:** `docker compose logs -f vayl`; scrape `/metrics` (Prometheus) from your monitoring.
185
+ - **Rotation:** rotate principal keys with `revoke_principal` + `create_principal`. For the master key,
186
+ rotate in Vault (`VAYL_KMS=vault`).
187
+
188
+ ## Security checklist (before go-live)
189
+
190
+ - [ ] TLS terminating in front; `:8080` not publicly exposed.
191
+ - [ ] `VAYL_ENCRYPT=on` and `VAYL_AUTH_REQUIRED=1` (defaults — confirm they weren't overridden).
192
+ - [ ] Admin key stored in your secrets manager; least-privilege roles for agents (`agent`/`member`, not `admin`).
193
+ - [ ] For regulated/sensitive data: `VAYL_KMS=vault`, and read `COMPLIANCE.md` (GDPR / AI-Act mapping) + `SECURITY.md`.
194
+ - [ ] Changed default Postgres/Neo4j passwords if you enabled those profiles.
195
+ - [ ] Backups of the data volume (and keys) tested.
196
+
197
+ ## Troubleshooting
198
+
199
+ | Symptom | Cause / fix |
200
+ |---|---|
201
+ | `remember`/`recall` error, or `health` shows `llm: FAIL` | LLM env wrong in `.env`. For Ollama-on-host, `OPENAI_BASE_URL=http://host.docker.internal:11434/v1` and the model is pulled. |
202
+ | Every call returns **401** | No/invalid `Authorization: Bearer vayl_sk_…`. Bootstrap an admin (step 4) and use its key. |
203
+ | Server won't start with `VAYL_KMS=vault` | Vault unreachable or the transit key missing — this is **fail-closed** by design. Fix `VAULT_ADDR`/`VAULT_TOKEN` / create the transit key. |
204
+ | `readyz` → 503 | Database not reachable (Postgres down, or bad `VAYL_DATABASE_URL`). |
205
+ | "Seat limit reached" on `create_principal` | Community cap hit — install an Enterprise license, or revoke an unused principal. |
206
+
207
+ Questions during onboarding? That's expected for a first deploy — reach your Vayl contact.