# Shared MCP Hub: user journeys, isolation and secrets UX

Status: requirements for review, not a claim of implemented or verified behavior.
Scope updated November 4, 2026: publish the **shared MCP Hub** first. Daedro
Secret is its credential-management subsystem, not a separate product the user
must operate. Dedicated per-user MCP infrastructure is a later release.

Evidence reviewed: `internal/secretsui/handler.go`,
`docs/connected-mcp-rollout.md`, current owner screenshots and the recorded
setup failures. No new browser walkthrough was performed for this review.

## Product promise

A person joins the Hub, chooses a profile and MCP, connects their account or
credential in the interface, and gets a useful result. Their credentials,
requests, uploads, jobs and results are inaccessible to other users. The MCP
runtime can be shared; caller authorization and data cannot be shared implicitly.

Ordinary users never run scripts, install Python, handle OpenBao root/unseal
material, restore IAP tunnels or remember an infrastructure-recovery passphrase.
Authentication is browser-based. Device/passkey confirmation establishes the
account session. In addition, every human secret operation requires a fresh,
action-bound Google Authenticator confirmation. A login code is not reusable
authority to manipulate secrets.

Use **MCP Hub** as the product entry, with **Profiles**, **Connections**,
**Activity** and **Settings**. Put **Credentials** under a connection and in
Settings for cross-connection management. Entering credential metadata requires
a fresh authenticator confirmation for that explicit profile-scoped view. OpenBao appears only in administrator
infrastructure diagnostics. Avoid making customers understand secret mounts,
AppRoles or technical policy paths.

## Domain vocabulary and boundaries

| Entity | Meaning | Authorization rule |
| --- | --- | --- |
| Account | A verified human identity and security factors | Stable server identity; not an email string supplied with a request |
| Workspace | Tenant boundary for membership and owned resources | Default new account gets one personal workspace; no automatic access to another workspace |
| Profile | A named working context inside a workspace, such as Personal or Client A | Credentials, connections and results belong to an explicit profile; switching the UI cannot grant access |
| Catalog entry | Public metadata for an available MCP and its reviewed operations | Contains no tenant credentials, object inventory or private usage data |
| Connection | A profile-scoped binding to an MCP, provider account and allowed operations | Uses exactly the caller-authorized credential reference; not a global mutable active credential |
| Credential | Secret material stored server-side plus safe metadata | Exact workspace/profile/owner permissions; never returned to the client merely to invoke a tool |
| Browser session | Short-lived authentication context | Resolves server-side account and authorized workspace/profile; contains no provider key or OpenBao root token |
| Invocation/job | One authorized operation and its immutable caller context | Carries account/workspace/profile/connection binding across synchronous and background processing |
| Result/upload | Data produced or consumed by a job | Same isolation contract as its parent invocation; opaque IDs are not authorization |

A profile is not a physical server. Do not use a **Private** checkbox to imply a
dedicated MCP instance in this release. If a sharing control is implemented,
label it **Only me** or an exact set of permitted members. **Dedicated runtime**
is a different feature and must be unavailable until its deployment works.

For initial public scope, recommend personal workspaces and profile isolation,
without collaboration grants. Team sharing can be added explicitly later.
Workspace administration must not silently mean permission to reveal all
members' credentials or transcripts. Define that policy before implementing teams.

## Main journey

### 1. Join and secure account

Entry: **Get started** from the public catalog.

Use the chosen verified account provider/invitation policy. Google sign-in, if
selected for public account creation, is different from Google Authenticator:
name the former **Continue with Google**, the latter **Authenticator code**.
Do not imply that scanning a TOTP QR proves an email identity or grants a tenant.

Offer **Create a passkey** in the browser/OS sheet. Supporting copy:
**Use Touch ID, your device PIN, or a security key.** Do not guarantee Touch ID
on every device or invent an invisible password fallback. A public deployment
must never assign installation administrator status to the first visitor.

Enroll Google Authenticator with an in-app QR and instructions:
**Open Google Authenticator → Add account → Scan a QR code.**
Secondary **Can’t scan?** reveals the setup key. Generate the QR locally;
never send the TOTP seed to an external QR image service or analytics.
One six-digit field supports paste and `one-time-code` autofill. Primary action:
**Verify and continue**. Account access requiring MFA is enabled only after a
valid code. Incorrect code retries stay on this screen; they do not erase a
completed passkey registration. Bound attempts and prevent OTP/challenge replay.

Do not replace one memorized password with a recovery-file password. Recommend a
backup passkey and a tested recovery route. Recovery codes, if used, are
one-time backup artifacts, not an extra normal-login password. Account recovery
must not imply that inaccessible OpenBao encryption material can be decrypted.

### 2. Choose profile

Create the first profile automatically as **Personal**, avoiding a mandatory
empty naming form. Show the current profile in the persistent application shell.
Users can rename it or create another profile, such as **Client A**.

A profile switch changes the connections, credential references, recent jobs and
results shown. It cancels or rebinds any unsubmitted operation safely. It never
silently executes a form prepared under the previous profile. A submitted job
keeps its original context even if the user changes profile while it runs.

Empty state: **Connect your first MCP**. Primary action: **Browse MCPs**.

### 3. Select MCP from the shared catalog

Categories should follow tasks: **Data & storage**, **Audio & transcription**,
**Video**, **Text & documents**, **Messaging & social**, and **Automation**.
Search by name or task. Do not duplicate an entry into a fake feature merely
because a category needs content.

Each card shows name, task, supported operations, credential requirement,
shared-service availability and charging information when known. Product badges
must be concrete: **Available**, **Setup required**, **Temporarily unavailable**.
Avoid an undifferentiated green **Ready** badge.

The detail page explains what data is sent to the provider, required access
scope, limits, whether an action can change external data, and the actual
supported runtime. Primary action: **Connect**. For unavailable services, show
why and provide a real next action; never offer a nonfunctional launch button.

### 4. Connect securely

Connection form contains only provider-required fields. Prefer provider OAuth
when supported; otherwise use a password-style credential field with a concise
scope hint. A provider key is entered once. Display the target profile directly
above the save action: **Save to Personal**.

If a caller already owns an authorized credential in this profile, offer
**Use an existing credential**. Do not list another user's credential names or
another profile's inventory without explicit authorization. A shared catalog
entry must never default to the last credential another user supplied.

Primary action: **Review and connect**. A confirmation sheet shows the profile,
connection, credential label and requested permissions, then asks for a fresh
Google Authenticator code. The code approves exactly that displayed operation
or explicit batch. The separate secret-module API stores the secret, grants the
reviewed scope and returns a reference; the Hub stores that reference, not the
credential. A harmless authenticated probe then returns a typed result. Retry is idempotent. A failed probe retains the secure
record where appropriate and lets the owner replace it; it does not redisplay
the secret value. A different existing value requires explicit update semantics.

Success: **Connected to S3** with the account/connection metadata safe for that
caller. Never claim **Transcription tested** after only a capabilities request.

Existing local MCP discovery is an operator migration facility, not a public
user onboarding requirement. Publish the reviewed catalog; do not scan a
customer's device, files or accounts without an explicit supported integration.

### 5. Perform a useful action

S3 first slice: **Browse buckets** → **Browse objects** → metadata/detail.
Results include bounded authorized names, sizes and timestamps, with prefix
filters and pagination. Return useful data, not only a generic successful-call
message. Keep upload/delete unavailable until their operation and confirmation
contracts are implemented.

Audio/video: **Upload audio** / **Upload video**, then supported provider/format
options and **Transcribe**. Show upload limits and external-processing context
before submission. Preserve a durable job across reload; show queued, running,
completed and failed states. Completed means a readable transcript/result is
available to the submitting principal, not merely that a job ID was returned.
Video should reuse the existing voice service rather than duplicate speech
provider credentials and behavior.

No arbitrary command, secret path, endpoint or tool name can be supplied by a
browser caller. The server selects reviewed operations and validates typed
arguments. Request IDs and opaque resource IDs are not access grants.

### 6. Results and activity

Activity is scoped to the current authorized profile. Each entry shows operation,
connection, time, outcome, duration and a safe diagnostic reference. Show clear
partial failures and retryability. Do not place prompts, secret fields, bearer
headers, full transcripts or provider error bodies in general telemetry.

Share/export is a separate, explicit operation. Do not create public result links
by default. Signed download URLs must be short-lived and scoped; possession of a
job ID must not retrieve another caller's upload or result. Browser back/forward,
cache and server rendering must not resurrect data after sign-out/profile change.

## Secret-module contract: confirm every human change

This is a hard product requirement, not optional login step-up. The Secret module
has its own API and authorization enforcement. Hub clients hold opaque references
and safe status only. The Hub must not return provider secrets to callers or
substitute its own session token for the required action confirmation.

| Human action | What the confirmation shows | Result |
| --- | --- | --- |
| Enter credential metadata | Workspace/profile and the metadata view being opened | Scoped metadata access; no secret values |
| Add credential | Target profile, credential label, provider, initial usage grants | New reference/version; value never returned |
| Replace or rotate credential | Exact credential and current version, affected connections and new scope | Updated reference/version, safe outcome |
| Delete credential | Exact target and affected dependent connections | Safe deletion result; no secret returned |
| Grant or change MCP access | Credential reference, MCP/connection, permitted operations, duration/scope | Exact approved grant |
| Revoke access | Grant target and affected MCP/connection | Revoked grant/status |

UI confirmation copy: **Confirm this change** → action summary → **Google
Authenticator code** → **Confirm [specific action]**. Do not prefill or replay a
code from sign-in. Do not show a raw secret value in the confirmation summary.
For add/replace, the browser submits the value securely but never receives it
back from the API, including errors or completion responses. Reveal and export
are unavailable in v1; do not ship disabled-looking reveal endpoints underneath.

The action challenge is bound server-side to actor, authenticated session,
workspace/profile, credential/reference/version, action, requested scope and an
expiry. A submitted OTP cannot approve a different body, changed target, later
operation or wider scope. Challenges are one-use. A body/version change requires
another review. Failed or canceled confirmation performs no mutation. Errors
retain safe form context; secret values are not persisted in browser storage.

Reduce clicks by allowing an **explicit batch** such as “Store this S3 credential
and grant this connection read-only bucket/object access.” Show every change and
target before the code. One code approves exactly that atomic batch, not a timed
period of unrestricted secret administration. If atomic execution cannot be
provided, expose precise partial results and safe retry semantics instead of
claiming all changes were applied.

Metadata entry confirmation is scoped to the displayed workspace/profile and
view, with a short explicit validity/expiry policy. Switching profiles or
requesting a different secret view must not reuse broader authority. Do not
prompt separately for each label in one approved metadata listing; do not turn
entry approval into indefinite global read permission.

An automatic MCP invocation does not ask for an OTP on every run. It can use only
a previously TOTP-approved exact grant, with fresh short-lived workload access.
Creating, widening, rotating or reassigning that grant is a new human secret
action and requires confirmation. A manual “Run” of an already-authorized
operation likewise cannot silently create a grant. The UI must distinguish
**Run operation** from **Allow this MCP to use a credential**.

## Shared runtime is not shared user state

The release specification must prohibit the following, including in adapters:

- Global `active_api_key`, global current-profile state, or a reused authenticated
  MCP session changing credentials between simultaneous callers.
- Result caches keyed only by operation/arguments without authorization context.
- Shared temporary filenames, uploads or working directories containing caller
  material, unless their ownership and access are explicitly isolated.
- Background jobs losing the original caller context, or workers reading secrets
  based only on untrusted payload workspace/profile fields.
- Cross-user listings, metadata leaks, error differences revealing foreign IDs,
  guessed download IDs, and support/admin dashboards returning user content by
  default.

The UX cannot compensate for an adapter that holds global credentials. Such an
adapter remains unavailable in shared mode until it has request-scoped identity
and isolated data, or an isolation mechanism appropriate for that adapter.
Dedicated per-user infrastructure is later scope, but the first shared release
still needs per-request/session/process isolation where the implementation
requires it. “Shared server” is not permission to merge tenants.

## Return login and recovery

Return login: **Continue with a passkey** → **Authenticator code** → previously
permitted page. Avoid a username/password form when discoverable credentials and
the selected identity provider support the intended flow. Keep the destination
and safe draft context through re-authentication; do not replay writes silently.

Passkeys prove possession of a private key; MAC addresses, device names and
hardware fingerprints are not equivalent authentication. Standard Apple passkeys
can sync across devices. Label them **Passkeys**; **My MacBook** is a user label,
not a cryptographically guaranteed single-device identity.
[Apple passkeys](https://developer.apple.com/passkeys/),
[Apple passkey security](https://support.apple.com/en-ca/102195).

Choose the stable public HTTPS relying-party domain before public enrollment.
Temporary IP-based pilot authentication must not be presented as portable public
account enrollment.

| Loss scenario | Supported path |
| --- | --- |
| Device lost; another passkey exists | Other passkey plus TOTP; revoke the lost credential |
| Phone changed; Authenticator restored/transferred | Existing passkey plus restored code |
| TOTP lost; passkey remains | Previously issued single-use recovery code replaces TOTP for recovery, then fresh enrollment |
| All passkeys lost | Established account-recovery policy; never TOTP-only owner promotion |
| Both factors lost | Explicit account recovery with defined assurance; no silent tenant or data reset |
| Infrastructure encryption keys lost | Administrator data-recovery procedure; unrelated to changing user factors |

A single-owner pilot without an independent recovery administrator cannot claim
complete recovery from a Mac-only Keychain copy. Do not force users to memorize
another password to hide this architectural limitation. Define and test a backup
route; communicate its boundary honestly.
[Google Authenticator transfer/sync](https://support.google.com/accounts/answer/1066447).

## Meaningful errors

| Condition | User message | Next action |
| --- | --- | --- |
| Passkey canceled | **Sign-in was canceled.** | **Try again** |
| Wrong TOTP | **That code did not match. Try the current code.** | Remain on code screen |
| Session expired | **Confirm it’s you to continue.** | Passkey/TOTP, preserve safe context |
| Credential rejected | **S3 rejected this credential.** | **Update credential** |
| Permission absent | **This credential cannot list buckets.** | Bucket-scoped option if actually supported |
| Service offline | **Audio service is temporarily unavailable.** | **Retry**, plus truthful service status |
| Secret store reconnecting | **The secure store is reconnecting. Your keys are unchanged.** | Automatic bounded recovery, then **Retry** |
| Secret store needs recovery | **The secure store needs administrator attention.** | Status/support; never a member unseal-key form |
| Foreign/missing resource | **This item is unavailable.** | Return to authorized list, same response shape |

Do not expose `URLError`, PEM errors or shell instructions. Distinguish provider
permission failures from wrong login factors. Do not retain credential values in
DOM or fill them into an update form after an error.

## Observability requirements

The public catalog may show aggregate availability of a shared MCP. It must not
show another tenant's invocation count, private connection identifiers, provider
account names, query details or output. User Activity is filtered server-side by
account/workspace/profile. Operational traces use safe identifiers and structured
error categories; their access is a separate administrator permission.

Existing observability capabilities need inventory before promising dashboards.
A log file or an unscoped server-wide events page is not a tenant-safe Activity
feature. Requirements must identify which current audit/log/metrics components
exist and which are new work. Do not add fictional MCPs to fill this category.

## First public release acceptance

Do not equate the current single-demo-workspace pilot with this contract. It has
a narrow connection-check UI, static operator mapping, local bearer processors,
serialized execution and manual infrastructure lifecycle. Its existing tests do
not prove isolation for the shared public Hub.

Required acceptance gates:

- Every credential add/replace/delete/rotation/grant/revocation and metadata-view
  entry is rejected without its fresh action-bound TOTP confirmation. Test code
  replay, changed targets/bodies/versions, cross-profile substitution, canceled
  operations, expired challenges and explicit batch atomicity. Login MFA alone
  never authorizes these requests. Background runs work only under the previously
  approved grant. Reveal/export endpoints do not exist in v1.

1. Two independently registered accounts create profiles and connections through
   the browser without scripts, manual certificate bypass or OpenBao knowledge.
2. Both can sign out and return through the specified passkey/TOTP flow; canceled
   and invalid attempts recover in place and replay/cross-origin requests fail.
3. A real S3 credential is stored through the subsystem, a real authorized API
   listing succeeds, and usable bounded results render without credential leaks.
4. Account A cannot list/read/update/delete B's credentials, profiles,
   connections, uploads, jobs, activity or results by guessed IDs or changed
   request fields. Repeat within A's separate profiles according to policy.
5. Concurrent A/B requests with distinct keys to the same shared MCP return only
   their own results. Check retries, cached responses, streaming, worker restarts,
   failures and browser profile switching, not only successful single calls.
6. UI/server/job-store restart preserves ownership and saved connections. Secret
   store reconnection is internal; credential requests are not repeated to mask
   an infrastructure outage. Failed startup cannot assign a new owner.
7. Real audio/video processing produces a durable authorized result before that
   catalog feature is marked verified. Capability discovery alone is insufficient.
8. Observability and support surfaces do not expose cross-tenant data. Secrets
   remain absent from page source, logs, traces, command arguments and Git.
9. Account/factor recovery is tested with a disposable account; storage recovery
   is tested separately. Factor reset never grants access to another tenant.
10. Old source credentials/data remain intact until replacement reads and restart
    behavior pass; retirement is explicit. Dedicated runtime controls are absent
    or clearly unavailable until a later scoped release.

## Smallest useful slice and rollout boundary

First implement and accept **public identity + personal workspace/profile + one
shared S3 connector + browser credential onboarding + real result browsing +
two-user isolation + restart behavior**. Keep all detected MCPs inventoried, with
truthful availability. Then apply the same contracts to audio/video, other data
adapters and operational surfaces. Do not expand eleven incomplete adapters in
parallel while the first tenant-isolated user journey is still broken.

Visual direction: a readable glass shell with persistent profile context,
compact authentication screens and a wider connections/results layout. Avoid
putting an entire marketplace into a narrow centered sign-in form. Validate
keyboard access, visible focus, 200% text zoom and a narrow viewport using the
actual implemented flow.

Public approval is about this complete journey and its isolation evidence, not
a pretty setup screenshot or unit-suite pass. The current user's previous setup
failed repeatedly at Python, tunnel and recovery-passphrase steps; the replacement
must remove those steps from the customer contract, not merely reword them.
