Data and privacy
Verified against v0.21.0 · internal/db/migrations, internal/store/users.go, web/src/lib/notificationAudio.ts, cmd/parley/main.go, internal/auth/oidc.go, internal/standup/webhook.go, internal/plugin/fetch.go, internal/plugin/secrets.go, web/src/lib/meet.ts
Parley stores very little about people, mostly because it never asks. In its default mode there is no account, no email address and no password.
Personal data, in full
Section titled “Personal data, in full”| Table | Field | What it is |
|---|---|---|
users |
id |
A generated UUID. Public — it is broadcast to everyone in a room |
users |
name |
Up to 64 characters. Self-chosen, or derived from an identity-provider claim |
users |
issuer, subject |
Only for federated sign-in: which provider vouched for you, and your opaque id there. Empty for anonymous users |
users |
avatar_icon |
The avatar you picked, as one short opaque id (≤32 characters). Empty until you choose one. Public — shown to everyone in a room. Removed with the account: it is a column on the users row, not a separate record |
users |
avatar_accessory |
Retired in v0.5.1: no longer written or read, and dropped by a later migration |
users |
notification_sounds |
Whether this user enabled local notification sounds. False by default and never included in rosters or session payloads |
users |
created_at |
Timestamp |
session_tokens |
token_hash |
SHA-256 of the bearer token. The token itself is never stored |
session_tokens |
user_id, created_at, last_used_at |
Which account, and when it was last seen |
members |
space_id, user_id, spectator, last_seen_at |
Which rooms you have joined |
session_presence |
session_id, user_id, replica_id, seen_at |
Which session you have a socket open on, and which replica is serving it. Written while you are connected, deleted when you disconnect, and swept once it ages past the presence window (~100s). It is what the space page counts to say “3 here” |
No email address is stored, even when signing in through a provider that supplies one — an email is used only to derive a display name, and then discarded. There is no avatar URL and no uploaded image — an avatar is a color derived from your user id plus, optionally, two ids naming a shape you picked from the built-in set — and no IP address column anywhere.
Content data
Section titled “Content data”Everything else is what a team types: session titles, story titles and references, notes up to 2000 characters, votes, and standup entries of up to 2000 characters each.
Treat this as the sensitive data. A story note is where somebody writes the customer’s name or the reason a deadline slipped. It is stored as plain text in Postgres, and it is what a database dump mostly contains.
What leaves the instance
Section titled “What leaves the instance”Nothing, unless you configure it. Parley has no telemetry, no analytics, no crash reporting and no update check.
Notification cues are synthesized in the browser with Web Audio. They use no audio files and make no network request. The server stores only the on/off preference.
The server connects to configured Postgres. In open mode with no webhooks or
network-capable plugins configured, it needs no external service. With
AUTH_MODE=oidc, it also contacts the configured IdP for discovery, signing
keys, and sign-in token exchange. Discovery is probed in the background at boot
without gating startup or readiness; a failed probe is retried during sign-in.
Configured standup webhooks send event metadata to their approved recipient; plugins with approved fetch grants can send requests to allowed HTTPS hosts. The optional Meet add-on loads Google’s SDK in the browser and uses its activity APIs. Review Integrations and outbound data for the current inventory and operator responsibilities. Self-hosting does not make an external connector or bring-your-own model endpoint local.
Retention and erasure
Section titled “Retention and erasure”| Capability | Status | What to do instead |
|---|---|---|
| Retention policy | Not built | Application records are not automatically deleted. Session tokens are the exception: they expire on the configured lifetimes and their rows are swept hourly. |
| Account deletion | Not built | There is no endpoint. DELETE /api/me ends a session, it does not delete the account. Deleting a users row must be done in SQL, and see the caveat below — it is not a clean cascade. |
| Per-space export or erasure | Not built | Export sessions to CSV individually; erase with SQL against the documented schema. |
| Expired token cleanup | Built in | An hourly background pass deletes session_tokens rows past either SESSION_IDLE_TTL or SESSION_MAX_TTL, so the table does not grow without bound. |
| Content encryption at rest | Not built | Meeting content and room codes are not encrypted by Parley. Use encrypted storage or your database provider's encryption. Plugin secrets and webhook signing secrets use AES-GCM with a configured key. Binding ciphertext to its plugin install/name or webhook space requires a successful parley secrets reseal; key custody remains the operator's responsibility. |
| Telemetry | Out of scope | There is none, and adding it is not planned. |
If you need to answer a data-subject request
Section titled “If you need to answer a data-subject request”There is no tooling for it. In practice: users gives you the person’s row,
members the spaces they joined, and their content is reachable by user_id
from votes and standup_entries.
members, session_tokens, session_presence, votes and standup_entries
all cascade on delete, so removing the users row takes them with it.
Verify against the schema before running anything destructive, and take a backup first.