Legal // PRIVACY
Privacy Policy
This page describes what the dev.pipe systems actually store. Every unmarked statement was read out of the source code of this service and can be checked against the files named beneath it. Blocks marked as a gap are legal or organisational statements that only the operator and their lawyer can make.
01Controller and data protection officer
⚠ MISSING — OPERATOR INPUT REQUIRED
Who is responsible for this processing
Required entries:
- Name, legal form and address of the controller (Art. 4 no. 7 GDPR)
- Contact address for data protection enquiries
- Whether a data protection officer has to be appointed (section 38 BDSG) and, if so, their contact details
- Representative under Art. 27 GDPR, if the controller is not established in the EU
Without these entries the whole policy has no addressee and no data subject can exercise a right.
02What the service does
You send a request from a development tool to an OpenAI-compatible endpoint. A gateway checks the API key and forwards the request to inference workers that hold the model in GPU memory. The control plane issues keys, counts usage and talks to Stripe for billing.
Verified in:
config/litellm-config.yaml · docker-compose.yml · backend/app/api/v1/
03Data stored in the control-plane database
The control plane keeps the following rows in PostgreSQL. The list is complete for personal data as of the state of this code base.
| Table | Stored fields | Note |
|---|---|---|
| customers | Email address, name, Stripe customer ID, active flag, created and updated timestamps | Written when a subscription is paid for. |
| api_keys | SHA-256 hash of the key, display prefix, key alias, key type, gateway key ID, rate limits, active flag, expiry, creation and revocation timestamps | The key itself is never stored. For a trial claimed without a client identifier the alias is derived from the requesting IP address, so the alias itself is personal data. |
| subscriptions | Stripe subscription ID, plan type, status, current billing period, timestamps | Mirror of the state Stripe reports by webhook. |
| device_authorizations | Email address in plain text, folded email identity, IP address in plain text, SHA-256 hash of a hardware fingerprint, SHA-256 digests of the device and user codes, attempt counters, timestamps | Belongs to the device flow of the CLI installer. The IP address is stored in plain text, not hashed. |
| event_ledger | Stripe event ID, event type, processing status, hash of the payload, processing timestamp | Stops a Stripe webhook from being processed twice. No payload content is kept. |
| usage_metrics_hourly | Counters only: requests, prompt, completion and cached tokens, latency buckets, throttled requests per hour and key | No address, no IP address, no request content. |
Verified in:
backend/app/models/
04Short-lived data in Redis
Redis holds only counters and markers, each with an expiry. The longest lifetime in the code is seven days.
- Abuse counters under hashed identifiers, so the identifier cannot be read back out of the counter
- Spend markers for the proof-of-work challenge, so a solved challenge cannot be replayed
- Usage aggregates that feed the cockpit charts
Verified in:
backend/app/services/rate_limiter.py · backend/app/services/telemetry_pipeline.py
05The content of your requests
Prompts, source code and generated answers are held only in memory for as long as a request runs. Four independent settings keep them out of storage, and they are listed here so the claim can be verified rather than believed.
- The gateway's only logging integration is our own usage forwarder, restricted to a fixed allowlist of token counters and key hashes; the gateway's own usage records likewise store no prompt or response text
- The inference workers are started with --disable-log-requests, so prompts never reach their log
- The reverse proxy strips the query string as well as request and response headers from its access log
- The error tracker replaces prompt, message and token fields with a filter marker before an event leaves the process
Because no request content is written to disk anywhere in this stack, there is no data set here that could be used to train or fine-tune a model.
Verified in:
config/litellm-config.yaml · config/litellm_usage_callback.py · docker-compose.yml · config/Caddyfile · backend/app/core/bugsink.py
06Web server access logs
The reverse proxy writes an access log to standard error. Query string, request headers and response headers are removed from every entry, which is what keeps API keys, session cookies and prompts out of it. The remaining fields still include the IP address of the requesting client.
⚠ MISSING — OPERATOR INPUT REQUIRED
Log retention
Required entries:
- How long container logs are kept and where they are shipped
- Whether log rotation is configured on the host; the compose files set no rotation limits
- Retention period for the IP addresses in these logs
The stack itself never deletes a log line. What happens to it is decided outside this repository.
Verified in:
config/Caddyfile
07Cookies and browser storage
This site sets exactly one cookie. It is called session_token, it is flagged HttpOnly and SameSite=Lax, it is sent with the Secure flag in production, and it expires after seven days. It exists so that a signed-in customer stays signed in. There is no analytics cookie, no advertising cookie and no tracking pixel, and the scripts use neither localStorage nor sessionStorage.
⚠ MISSING — OPERATOR INPUT REQUIRED
Consent assessment
Required entries:
- Whether the login cookie is strictly necessary in the sense of section 25 (2) TDDDG and therefore needs no consent banner
The technical picture above is complete. The conclusion drawn from it is a legal one.
Verified in:
backend/app/api/v1/auth.py · frontend/static/js/
08Fonts and other external resources
The web fonts are delivered from this server. Opening a page of this site therefore contacts no third-party server at all, and no IP address is disclosed to a font provider. A test keeps it that way for every public page.
Verified in:
frontend/static/css/input.css · frontend/static/fonts/ · tests/test_frontend_assets.py
09Recipients and third parties
These are the only recipients that appear in the code. The legal basis column is deliberately empty.
| Recipient | What is transmitted | When | Legal basis |
|---|---|---|---|
| Stripe (payment processing) | Email address, name, chosen plan, and the hash and prefix of a trial key that is being upgraded | When a checkout session is created and whenever Stripe reports a subscription event | — TO BE COMPLETED — |
| SMTP provider (not named in the code) | Recipient address, subject and body of verification and sign-in emails | Whenever such an email is sent | — TO BE COMPLETED — |
| Bugsink error tracking (optional, self-hosted) | Exception data with prompts, keys, tokens and cookies replaced by a filter marker; request path and status code | Only when a server error occurs and only if the operator configured a DSN | — TO BE COMPLETED — |
| Hugging Face (model weights) | No customer data. The operator downloads model weights from there when a worker starts | Never during a customer request | Not applicable |
⚠ MISSING — OPERATOR INPUT REQUIRED
Details only the operator knows
Required entries:
- Which company actually sends the email, and where it processes the address
- Which Stripe entity the contract is with and which transfer safeguards apply
- Whether the error tracker is switched on in production and on whose infrastructure it runs
- Hosting provider and data centre operator as a further processor
- The legal basis for each row of the table above
Card details are entered on a Stripe page and never reach this system; everything else above depends on contracts the code cannot see.
Verified in:
backend/app/services/stripe_service.py · backend/app/services/email_service.py · backend/app/core/bugsink.py · docker-compose.yml
10Automated checks on trial requests
A trial key is issued without a credit card, which is why the system decides automatically whether a request looks legitimate. It compares the IP address against published ranges of cloud providers, rejects disposable mail domains, requires a proof-of-work solution and a confirmed email address, and grants one trial per address within a waiting period. A request that fails these checks is refused without a human looking at it.
⚠ MISSING — OPERATOR INPUT REQUIRED
Assessment of the automated decision
Required entries:
- Whether this refusal amounts to an automated decision under Art. 22 GDPR and what has to be disclosed about it
- How a person can ask for the decision to be reviewed
The mechanics above are described completely. Whether they are lawful in this form is a legal judgement.
Verified in:
backend/app/services/datacenter_feed_service.py · backend/app/services/rate_limiter.py · backend/app/services/onboarding_service.py
11Storage period and erasure
This has to be stated plainly, because it is the point where the system does not yet do what a privacy policy usually promises: the backend contains no automated deletion path. No code path removes a row from the database. A revoked API key is flagged as inactive and given a revocation timestamp; its row, including the alias that may contain an IP address, stays. Only the entries in Redis remove themselves, after at most seven days.
⚠ MISSING — OPERATOR INPUT REQUIRED
Retention periods and erasure process
Required entries:
- Retention period for each table listed above
- How a request under Art. 17 GDPR is carried out today, who does it and how it is documented
- Which rows have to be kept for tax or commercial law and for how long
- Whether an erasure routine will be built before launch
Do not write a retention period into this page that no code enforces. Either the routine gets built or the page says what really happens.
Verified in:
backend/app/services/stripe_service.py · backend/app/api/v1/dashboard.py · backend/app/models/
12Purposes of the processing
The purposes are taken from the code. The legal basis for each of them is a legal assessment and is left open.
| Purpose | Data used | Legal basis |
|---|---|---|
| Provide the inference service and manage API keys | Key hash, prefix, alias, limits, customer reference | — TO BE COMPLETED — |
| Issue trial access and keep it from being abused | Email address, IP address, hardware fingerprint hash, counters | — TO BE COMPLETED — |
| Billing, subscription management and invoices | Email address, name, Stripe identifiers, plan, status | — TO BE COMPLETED — |
| Send verification and sign-in emails | Email address, one-time link or code digest | — TO BE COMPLETED — |
| Operate the platform securely and find errors | Access logs including IP address, scrubbed error events | — TO BE COMPLETED — |
| Show usage figures in the cockpit | Hourly counters per key, no content | — TO BE COMPLETED — |
13Your rights
Under the GDPR you may request access to your data (Art. 15), correction (Art. 16), erasure (Art. 17), restriction of processing (Art. 18) and a copy in a portable format (Art. 20), you may object to processing based on a legitimate interest (Art. 21), and you may lodge a complaint with a supervisory authority (Art. 77).
⚠ MISSING — OPERATOR INPUT REQUIRED
How to exercise these rights
Required entries:
- Address or mailbox for such requests
- Competent supervisory authority, which follows from the seat of the business
- Internal process and response deadline
- How an erasure request is satisfied while no deletion path exists
A list of rights without an address to send them to is of no use to anyone.