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.
- vayl_mcp-0.1.0/COMPLIANCE.md +222 -0
- vayl_mcp-0.1.0/CONTRIBUTING.md +38 -0
- vayl_mcp-0.1.0/DEPLOY.md +207 -0
- vayl_mcp-0.1.0/LICENSE +201 -0
- vayl_mcp-0.1.0/MANIFEST.in +4 -0
- vayl_mcp-0.1.0/NOTICE +40 -0
- vayl_mcp-0.1.0/PKG-INFO +542 -0
- vayl_mcp-0.1.0/README.md +503 -0
- vayl_mcp-0.1.0/SECURITY.md +187 -0
- vayl_mcp-0.1.0/benchmarks/__init__.py +1 -0
- vayl_mcp-0.1.0/benchmarks/clinical/__init__.py +1 -0
- vayl_mcp-0.1.0/benchmarks/clinical/patients.py +421 -0
- vayl_mcp-0.1.0/benchmarks/clinical/run.py +274 -0
- vayl_mcp-0.1.0/benchmarks/common/__init__.py +1 -0
- vayl_mcp-0.1.0/benchmarks/common/llm_client.py +108 -0
- vayl_mcp-0.1.0/benchmarks/common/metrics.py +138 -0
- vayl_mcp-0.1.0/benchmarks/common/schema.py +88 -0
- vayl_mcp-0.1.0/benchmarks/common/vayl_client.py +260 -0
- vayl_mcp-0.1.0/benchmarks/evaluations/compare_systems.py +317 -0
- vayl_mcp-0.1.0/benchmarks/evaluations/eval_adversarial.py +181 -0
- vayl_mcp-0.1.0/benchmarks/evaluations/eval_graph.py +114 -0
- vayl_mcp-0.1.0/benchmarks/evaluations/eval_reconcile.py +119 -0
- vayl_mcp-0.1.0/benchmarks/evaluations/graph_headtohead.py +226 -0
- vayl_mcp-0.1.0/benchmarks/evaluations/messy_eval.py +136 -0
- vayl_mcp-0.1.0/benchmarks/evaluations/rep_eval.py +51 -0
- vayl_mcp-0.1.0/benchmarks/evaluations/retraction_battery.py +268 -0
- vayl_mcp-0.1.0/benchmarks/evaluations/scale_bench.py +297 -0
- vayl_mcp-0.1.0/benchmarks/load/__init__.py +1 -0
- vayl_mcp-0.1.0/benchmarks/load/concurrency.py +263 -0
- vayl_mcp-0.1.0/benchmarks/load/integrity.py +210 -0
- vayl_mcp-0.1.0/benchmarks/locomo/__init__.py +1 -0
- vayl_mcp-0.1.0/benchmarks/locomo/prompts.py +283 -0
- vayl_mcp-0.1.0/benchmarks/locomo/run.py +575 -0
- vayl_mcp-0.1.0/benchmarks/scripts/run_eval_gpt4omini.sh +37 -0
- vayl_mcp-0.1.0/benchmarks/stress/stress_test.py +90 -0
- vayl_mcp-0.1.0/docs/README.md +12 -0
- vayl_mcp-0.1.0/pyproject.toml +79 -0
- vayl_mcp-0.1.0/setup.cfg +4 -0
- vayl_mcp-0.1.0/src/vayl/__init__.py +2 -0
- vayl_mcp-0.1.0/src/vayl/api/__init__.py +0 -0
- vayl_mcp-0.1.0/src/vayl/api/mcp_server.py +954 -0
- vayl_mcp-0.1.0/src/vayl/api/server.py +267 -0
- vayl_mcp-0.1.0/src/vayl/auth/__init__.py +0 -0
- vayl_mcp-0.1.0/src/vayl/auth/auth.py +206 -0
- vayl_mcp-0.1.0/src/vayl/auth/sso.py +133 -0
- vayl_mcp-0.1.0/src/vayl/clinical/__init__.py +1 -0
- vayl_mcp-0.1.0/src/vayl/clinical/fhir.py +167 -0
- vayl_mcp-0.1.0/src/vayl/clinical/medrec.py +186 -0
- vayl_mcp-0.1.0/src/vayl/licensing/__init__.py +0 -0
- vayl_mcp-0.1.0/src/vayl/licensing/license.py +136 -0
- vayl_mcp-0.1.0/src/vayl/licensing/mint_license.py +79 -0
- vayl_mcp-0.1.0/src/vayl/licensing/receipts.py +130 -0
- vayl_mcp-0.1.0/src/vayl/memory/__init__.py +0 -0
- vayl_mcp-0.1.0/src/vayl/memory/decisions.py +162 -0
- vayl_mcp-0.1.0/src/vayl/memory/llm_memory.py +1289 -0
- vayl_mcp-0.1.0/src/vayl/memory/orgmemory.py +59 -0
- vayl_mcp-0.1.0/src/vayl/memory/reconcile.py +424 -0
- vayl_mcp-0.1.0/src/vayl/memory/schema.py +189 -0
- vayl_mcp-0.1.0/src/vayl/security/__init__.py +0 -0
- vayl_mcp-0.1.0/src/vayl/security/audit.py +203 -0
- vayl_mcp-0.1.0/src/vayl/security/crypto.py +171 -0
- vayl_mcp-0.1.0/src/vayl/security/kms.py +88 -0
- vayl_mcp-0.1.0/src/vayl/security/safety.py +54 -0
- vayl_mcp-0.1.0/src/vayl/storage/__init__.py +0 -0
- vayl_mcp-0.1.0/src/vayl/storage/db.py +204 -0
- vayl_mcp-0.1.0/src/vayl/storage/graph_store.py +232 -0
- vayl_mcp-0.1.0/src/vayl/storage/store.py +407 -0
- vayl_mcp-0.1.0/src/vayl/telemetry/__init__.py +0 -0
- vayl_mcp-0.1.0/src/vayl/telemetry/metrics.py +92 -0
- vayl_mcp-0.1.0/src/vayl_mcp.egg-info/PKG-INFO +542 -0
- vayl_mcp-0.1.0/src/vayl_mcp.egg-info/SOURCES.txt +94 -0
- vayl_mcp-0.1.0/src/vayl_mcp.egg-info/dependency_links.txt +1 -0
- vayl_mcp-0.1.0/src/vayl_mcp.egg-info/entry_points.txt +4 -0
- vayl_mcp-0.1.0/src/vayl_mcp.egg-info/requires.txt +21 -0
- vayl_mcp-0.1.0/src/vayl_mcp.egg-info/top_level.txt +1 -0
- vayl_mcp-0.1.0/tests/test_accountability.py +541 -0
- vayl_mcp-0.1.0/tests/test_api.py +245 -0
- vayl_mcp-0.1.0/tests/test_apply.py +733 -0
- vayl_mcp-0.1.0/tests/test_auth.py +440 -0
- vayl_mcp-0.1.0/tests/test_clinical.py +396 -0
- vayl_mcp-0.1.0/tests/test_confirmation.py +191 -0
- vayl_mcp-0.1.0/tests/test_db.py +66 -0
- vayl_mcp-0.1.0/tests/test_events.py +320 -0
- vayl_mcp-0.1.0/tests/test_graph_store.py +168 -0
- vayl_mcp-0.1.0/tests/test_http_pool.py +83 -0
- vayl_mcp-0.1.0/tests/test_licensing.py +173 -0
- vayl_mcp-0.1.0/tests/test_locomo_harness.py +251 -0
- vayl_mcp-0.1.0/tests/test_metrics.py +81 -0
- vayl_mcp-0.1.0/tests/test_orgmemory.py +159 -0
- vayl_mcp-0.1.0/tests/test_postgres.py +152 -0
- vayl_mcp-0.1.0/tests/test_recall.py +327 -0
- vayl_mcp-0.1.0/tests/test_reconcile_engine.py +93 -0
- vayl_mcp-0.1.0/tests/test_safety.py +130 -0
- vayl_mcp-0.1.0/tests/test_security.py +353 -0
- vayl_mcp-0.1.0/tests/test_slots.py +448 -0
- 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).
|
vayl_mcp-0.1.0/DEPLOY.md
ADDED
|
@@ -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.
|