Verified against v0.10.0 · internal/db/db.go, cmd/parley/main.go, ROADMAP.md, internal/api, internal/api/metrics.go, internal/hub/hub.go, internal/poker/routes.go, .github/workflows/ci.yml, .github/workflows/release.yml, .github/dependabot.yml
Every gap in the product, collected, so that the rest of this documentation is
worth believing. Read it before you deploy, not after.
If something here matters more to you than something on the
roadmap, start a Discussion. Issues track work after it is
accepted and actionable.
| Capability | Status | What to do instead |
|---|
| Operator administration UI | Not built | Installing a plugin, approving a pending upgrade, and disabling one are store operations with no screen behind them yet. An operator drives them from the database until that lands. |
|---|
| Plugin UI | Built in | A plugin can draw a panel, served from /plugin-ui/<name>/<version> and rendered in a sandboxed iframe with no network access of its own — see Plugin sandbox. |
|---|
| A plugin registry or signature verification | Not built | An operator places a bundle in PLUGIN_DIR and approves its grants; that approval is the whole trust decision. There is nothing to verify a bundle against. |
|---|
| Python plugins | Not built | Python is not an official Extism guest PDK. Rust, JavaScript, Go (TinyGo), Haskell, AssemblyScript, C, Zig and .NET are. |
|---|
| SQL for plugins | Not built | Deliberately absent and not planned. A plugin that could reach the pool would have every other capability by implication. |
|---|
| Capability | Status | What to do instead |
|---|
| Multiple replicas | Built in | Fanout runs over Postgres LISTEN/NOTIFY, presence and the room-code throttle are rows in Postgres, and simultaneous boots serialize their migrations behind an advisory lock. The socket hub is still per pod, but nothing depends on one pod seeing every socket. Not yet exercised on a real multi-node cluster. |
|---|
| Zero-downtime deploys | Built in | The chart rolls with maxSurge: 1 / maxUnavailable: 0 above one replica. During the rollout old pods serve against the newer schema the new pod migrated to; migrations are additive, so that is fine, and it resolves as the rollout replaces them. At replicaCount: 1 there is still a reconnect. |
|---|
| Connection migration during deploy | Not built | Shutdown closes a pod's WebSockets rather than handing them over; clients reconnect and land on a surviving replica. The reconnect is visible, the state is not lost. |
|---|
| Published performance numbers | Not built | There is no benchmark, and inventing one would be dishonest. Watch Postgres first — that is where the pressure lands. |
|---|
| Capability | Status | What to do instead |
|---|
| Metrics | Built in | /metrics is off by default. METRICS_ENABLED=true mounts an unauthenticated Prometheus exposition on the same port as the app. Keep it off a public ingress; scrape from the cluster network. See Observability. |
|---|
| Tracing and HTTP access log | Not built | There is no tracing and no HTTP access log. Blackbox-probe /readyz, and take request metrics and access logs from your reverse proxy. Every response does echo X-Request-Id — see Observability. LOG_LEVEL=debug adds one per-request line naming the socket peer, the X-Forwarded-For chain and the resolved client address — a proxy-diagnosis aid, not an access log: it records no method, path, status or user, and is not retained. |
|---|
| Backups | Not built | Nothing backs itself up. Schedule pg_dump. |
|---|
| Data retention or cleanup | Not built | Application records are never deleted automatically. Expired session_tokens rows are the exception: an hourly pass deletes them. |
|---|
| Down-migrations or rollback | Not built | Migrations are forward-only and the binary refuses to start against a newer schema. Roll back by restoring a backup taken before the upgrade. |
|---|
| A bind-address setting | Built in | BIND_ADDR prepends a bare host or IP address (IPv4 or IPv6 literal) to PORT. Empty (the default) still listens on every interface. It is not a Helm chart value — Kubernetes pods bind all interfaces and NetworkPolicy is the control. See Configuration. |
|---|
| Configuration reload | Not built | Everything is read once at boot. Restart to change anything. |
|---|
| Capability | Status | What to do instead |
|---|
| Space roles beyond owner and member | Not built | The owner role ships and gates membership management — see Authorization — but there is no admin or read-only role, and any member can still rotate the passcode or open the space. Story mutations and session controls are facilitator-only; ordinary members retain voting, their own standup entries, and their own standup readiness signal. |
|---|
| Per-user or per-team space access | Not built | A passcode gates a space; identity does not. Gate the whole instance with an SSO proxy if you need this today. |
|---|
| Removing a member | Built in | An owner removes a member from the space; their next request sees the stranger view and their sockets for that space are closed. It is not a ban — rotate the passcode too, or they can rejoin with it. The last owner cannot be removed. |
|---|
| Guest links | Built in | A facilitator mints a link to one room; whoever opens it takes a seat there and nowhere else, for 24 hours or until it is revoked. The URL is the credential — anyone holding it is the holder. A guest works both room kinds the same way as anybody else: they vote fully in poker, and in a standup take a place in the round and get the turn when it comes round to them. Starting, advancing and skipping the round stay the facilitator's. See Guest links. |
|---|
| A guest seat that survives a closed tab | Not built | A guest-link cookie is scoped to the browsing session — the browser, not the tab. Close the room tab while any other window of that browser is open and the cookie is untouched, so the next person to open the room URL is seated as that guest until the link expires. The cached name and hue live in sessionStorage and do die with the tab, but the room recovers the identity from the cookie. Quitting the browser ends the seat, unless it is set to restore its last session — "continue where you left off" restores session cookies along with the tabs. Nothing server-side can tell any of this apart from an ordinary request. Ending the seat on pagehide was considered and rejected: it fires on an ordinary refresh and on mobile backgrounding, so it would strand a guest who did nothing wrong. Use Leave room, or revoke the link, when the machine is borrowed or shared. |
|---|
| Account linking when switching to OIDC | Not built | Anonymous and federated identities are distinct. Existing history remains under the old display records; federated users rejoin spaces with their passcodes. |
|---|
| Sign out everywhere or admin revocation UI | Not built | Delete rows from session_tokens in SQL. HTTP rejects them immediately; WebSockets start a check every 30 seconds, allow a 30-second timeout, and close with code 1008 on failure. A space owner can remove someone from that space, but that revokes no token and reaches no other space. |
|---|
| Account deletion or data-subject tooling | Not built | SQL against the documented schema; note that deleting a user who facilitated a session is blocked by a foreign key. |
|---|
| Reading the audit log | Not built | Custody and plugin-admin actions are recorded in org_audit_log. Nothing reads that table: there is no API route and no screen, so reviewing it means querying Postgres. Sign-in, membership, passcode and guest-link events are not in that table; they (and the custody writes) each emit one stdout JSON security event line — see Observability. A product reader for the table is #399. |
|---|
| Creating an org | Not built | The default org is created by the migration; a second one is an insert into orgs in SQL. Membership then follows from the claim value you register on it. See Organizations and claim mapping. |
|---|
| Listing or revoking link guests as an org admin | Not built | A redeemed signed link mints an identity in no org and no space, so it appears on no members screen and org revocation cannot reach it. Its expiry and the session facilitator's revoke are the controls. This is by design, and the reason an org's member list is not a census of who can reach a room — see Organizations. |
|---|
| Capability | Status | What to do instead |
|---|
| SBOM and provenance | Built in | Releases produced by the hardened workflow include an image SBOM and a source SBOM, digest-qualified deployment manifests, maximum build provenance, and a GitHub attestation for the final GHCR digest. The SBOMs themselves are unsigned assets; the attestations are the verifiable part. Releases v0.1.0 through v0.2.1 were not backfilled. See Supply chain. |
|---|
| Vulnerability scanning in CI | Built in | govulncheck, CodeQL, dependency review, npm audit, and container scanning gate changes. |
|---|
| Automated dependency updates | Built in | Dependabot checks Go, web, site, Actions, and Docker dependencies weekly. |
|---|
| HSTS and several modern headers | Not built | Parley sets CSP, nosniff, X-Frame-Options and Referrer-Policy. Add HSTS, Permissions-Policy and the COOP/COEP/CORP family at your proxy. |
|---|
| General ingress rate limiting | Not built | Room-code guesses and open identity creation are throttled; spaces, sessions, and stories have configurable caps. General API calls and WebSocket connections still need ingress controls. |
|---|
| Encryption at rest | Not built | Use an encrypted volume or your provider's encryption. |
|---|
| Passcodes hashed at rest | Out of scope | Deliberate: a code is meant to be readable by members and passed on. A database dump therefore discloses every code. See the threat model. |
|---|
| Capability | Status | What to do instead |
|---|
| Retrospectives, story mapping, and other ceremonies | Not built | Poker and standup are the two session kinds. The rest is roadmap, and much of it is waiting on session kinds becoming a real extension point. |
|---|
| A verified mobile layout | Not built | Foundation in progress (#475): named breakpoints, touch targets on key controls, and safe-area insets on the shell and hand. Participant voting is the first-class mobile target; standup, settings, and facilitator workflows are not yet verified on a handset. |
|---|
| Custom decks | Built in | Four built-in decks, plus any a space owner saves under Settings → Decks. A saved deck is a template: a session copies the cards when it is created, so editing or deleting a deck leaves existing rooms alone. Sharing a deck between spaces is not built. See Planning poker. |
|---|
| Configurable standup timer | Not built | The server takes a secondsPerPerson value but no UI sets it, so 90 seconds is effectively fixed. |
|---|
| Undoing a deletion | Not built | A space owner can delete a space or a session outright, and it cascades. There is no soft delete and no undo — restore from a backup. |
|---|
| Filtering session history by date | Not built | Search by title, filter by kind, sort by recency. |
|---|
| Export formats beyond CSV | Not built | Parse the CSV or read the API. |
|---|
| Integrations, external notifications, webhooks | Not built | Parley can play local in-browser session cues, but nothing is pushed to another service or device. Copy the blockers roundup by hand, or poll the API. |
|---|
| Capability | Status | What to do instead |
|---|
| Telemetry or analytics | Out of scope | Parley phones nobody, and that will not change. |
|---|
| A hosted multi-tenant SaaS in this codebase | Out of scope | The room-code model and the space-per-team shape are built for self-hosting. |
|---|
| Non-Postgres databases | Out of scope | Postgres only, deliberately — one driver, one dialect, one set of behaviours to reason about. |
|---|
| In-app video or chat | Out of scope | Use the call you are already on. |
|---|