V8 Central — Product & Technical Spec
  1. Docs
  2. Frontend spec (from backend doc)
  3. FE: Integrations (Phase 5)

#FE: Integrations (from backend Phase 5)

Frontend spec for backend Phase 5: Integrations — requirements complete 2026-10-03; B5.1 cloud-provider account list built by Pair 1 (2026-10-10). B5.2 Connect, B5.3 Edit and B5.4 on-demand Check are built on Pair1; B5.5 Disconnect endpoint partially built, server-usage guard/warning pending Phase8; remaining Git/storage activity pending; Git/storage features depend on Servers. V8 requirement It covers three things that users think of as "connected accounts".

IntegrationWhere it lives (backend decision "Option B", 2026-10-03)What that means for the frontend
Cloud server providers — used to create serversCentral — organization level (D-27 resolved)A normal Central screen: list, connect, disconnect. Central stores the credentials encrypted
Git accounts — used to deploy applicationsEach server's OSS panelThe form is in Central, but the token is passed through to the chosen servers. Central keeps only a note (name, provider, which servers). OSS never gives a saved secret back
Backup storage — used for backupsEach server's OSS panelSame as Git

i18n namespaces: integrations.providers.*, integrations.git.*, integrations.storage.*, integrations.create.*, integrations.errors.*. Permissions: organization-level for the provider screens; the OSS git and storage permissions for the per-server parts (see Roles & permissions).

#A. Cloud server providers (Central) — /integrations

#FeatureScreen / controlRules from backend docFrontend notes
5.1List/integrations → cloud-provider accounts✅ Built Pair 1, 2026-10-10, organization-scoped, integration-viewName, translated provider/status, creation/update time. Search + provider/status filters; 10/page, max 50. No credentials or server usage count. Connect/Edit built on Pair1; Disconnect endpoint partial; usage guard pending
5.2Connect"Connect provider" → picker → one short formGive the account a name, then paste the token. Vultr, DigitalOcean, Linode, Hetzner: an API token (no OAuth login). AWS Lightsail: access key + secret key. Checked on save, stored encrypted. More than one account per provider is allowed V8 requirement 2026-10-03One flow for every provider — a modal with a name field and a token field (two for Lightsail), never a redirect. The name is what the user picks from later, so it is required and shown everywhere. Several accounts per provider means the list groups by provider and the picker shows names, not just logos. Help text: where to create the token. Saving shows "Checking…", then the provider's own error if the token is wrong. Write-only secret: after saving show only a masked hint
5.3EditAccount row → Edit modal✅ Built Pair1,2026-10-10: name and/or complete replacement credentials; provider immutable; manage permissionRename preserves status; omitted credentials retained; failed replacement changes nothing;409→reload/review, no overwrite
5.4Check saved credentialsAccount row → Check; Needs new token → Edit✅ Built Pair1,2026-10-10: manage-only, on-demand, no automatic monitoring200 can mean Connected or Needs new token;503 preserves status; unreadable credentials→Edit;409→reload, never stale overwrite
5.5DisconnectAccount row → confirmationEndpoint built, feature partial on Pair1; manage permissionRemoves selected Central account only; no upstream revoke/server delete. Required server-usage blocker and server-name warning unbuilt until Phase8; not fully READY

Update 2026-10-27 (Pair 2 review fixes): a cloud account still used by a server can't be disconnected — the API answers 409 cloud_account_in_use. Show "This cloud account is used by a server and can't be disconnected." and keep the row. A personal API token can't change cloud accounts.

#Integrations — Cloud-provider list (B5.1 built contract)

Main sidebar → Integrations → cloud-provider list (/integrations). Namespaces: integrations.providers.*, integrations.errors.*, common.*. Requires integration-view; Owner/Admin have access, custom roles need the grant. Active unverified users may list; banned accounts cannot.

API: GET /api/v1/organizations/{organization}/cloud-provider-accounts with login access token. Query: page (min 1), per_page (1–50, default 10), search (name substring, max 100), provider, status. All filters combine. %, _, ! and backslashes are literal search characters, not wildcards.

  • Providers: vultr, digitalocean, linode, hetzner, lightsail.
  • Statuses: connected, needs_new_token. Use returned provider_label and status_label (translated via Accept-Language, English fallback).
  • Rows: id, name, provider, provider_label, status, status_label, created_at, updated_at; links and meta for pagination. Newest creation first, then ID.
  • Pagination links preserve approved list filters only; do not append unrelated or credential-like query values.
  • No token fields, masked secret hints, server IDs or “Used by N servers” data in this response. Do not invent those values. Listing does not revalidate connectivity: display the saved status, not “checked just now”.
  • Connect and Edit use published B5.2/B5.3 APIs on Pair1; Disconnect endpoint now built partially; server-usage guard/warning not built. “Needs new token” can now offer Edit to integration-manage users; a successful replacement restores Connected. B5.4 now offers an explicit Check; automatic/background detection remains unimplemented.
  • Loading, accounts shown, no accounts, no matching results (Clear filters), request error with Retry, permission refused and organization missing. On organization change clear stale rows, filters/page and load only the new context.
  • Empty state may offer Connect to integration-manage users on Pair1, and Pair2 now reports pulling this code through cf36220; distinguish no accounts from no matches. 401 → Login; 403 → access/banned refusal; 404 → refresh organization context; 422 → field/query validation; 429 → back off (shared 120/min/user limit).
  • Done when permitted/denied and nonmember cases work; search treats special characters literally; provider/status filters and paging combine correctly; rows/counts never leak another organization's accounts; no secret/usage values are fabricated; Disconnect is presented only with its explicit partial-readiness restriction; never claim unused-server safety.

Source: Pair 1 API reference §44. Backend reports 557 Pest tests and quality/migration/isolation checks passed; this doc sync has not rerun them. Pair2 now reports B5.1–B5.5 pulled through cf36220; this supersedes the earlier doc-only report. No new product conflict; missing usage counts are a contract gap, not permission to build an invented endpoint.

#Integrations — Connect cloud provider (B5.2)

Main sidebar → Integrations → Connect MODAL (proposed UI), requires manage on organization.integrations.cloud-providers. POST /api/v1/organizations/{organization}/cloud-provider-accounts on Pair1, reference§45. Name required (max100); provider one of the five list options. For Vultr/DigitalOcean/Linode/Hetzner send credentials.token; Lightsail sends credentials.access_key + credentials.secret_key, not temporary STS/session tokens. No OAuth, custom endpoint, extra credential keys or client status/organization ID. Secrets must never enter URLs, persistence or logs.

Submitting → validating →201 metadata-only success → close and refresh list; connection activity now appears in account/organization history. Rejected credentials/missing verification permission →422 cloud_provider_credentials_rejected; keep modal and explain correction. Timeout/outage/rate-limit/invalid provider response →503 cloud_provider_unavailable; say temporary failure, not revoked token. Neither failure saves an account. Permission loss or organization deletion while saving → refuse and refresh context.429 → respect Retry-After (10/min/user across organizations).401→Login;403→manage/banned refusal;404→organization unavailable.

DigitalOcean needs account:read; Linode view_profile; Hetzner token is project-scoped. Successful read-only verification does not prove later server-create permissions. Multiple accounts/same names allowed; POST is not idempotent, so after a lost response refresh before retrying, avoiding duplicates. States: form, submitting, validation error, transient failure, permission/context changed, success. Edit is now built; on-demand Check is built, but automatic/background detection not built; Disconnect endpoint is partial until its usage guard lands.

Done when five field sets, manage/view/outsider cases, rejected/transient failures, lost-response refresh and secret boundaries are tested. Backend success tests used mocks; no genuine provider success has been verified. Pair2 reports this code pulled through cf36220.

#Integrations — Edit cloud-provider account (B5.3 built)

Proposed UI: /integrations → own organization account row → Edit MODAL. Manage on organization.integrations.cloud-providers (was organization.integrations.cloud-providers, renamed 2026-10-17); Owner/Admin/custom manage permitted, view-only refused. Unverified active users allowed. Pair1 API reference§46: PATCH /api/v1/organizations/{organization}/cloud-provider-accounts/{cloudProviderAccount}. Namespaces integrations.providers.*, integrations.errors.*, common.*.

  • Prefill name; provider read-only. Credential fields empty, “Leave unchanged to keep saved credentials”. Do not submit empty fields, nulls or masked values. Provider/organization/status/ID/verification URL cannot be changed. At least name or complete credentials required; name nonempty max100.
  • Rename only: omit credentials. No provider check and no status reset. Renaming a Needs new token row does not repair it. Same-name-only save is a no-op (no new time/history entry).
  • Replace credentials: token for Vultr/DigitalOcean/Linode/Hetzner; complete access key+secret pair for Lightsail, never partial merge. Same strict field sets as Connect. Can combine replacement with rename. Validate new credentials →200 safe metadata, same account/provider/organization →close and refresh list. Success sets Connected; no secret returned.
  • Replacement can repair unusable saved credentials without displaying them. Entering even the same token initiates verification/update; never compare against an exposed saved value.
  • 422 rejected credentials / 503 temporary provider failure: retain the modal error and explain that the old name, saved credentials and status were not changed. If rename and credentials were submitted together, rename did not save separately. Do not mark the old token revoked because verification of a replacement timed out.
  • 409 cloud_provider_account_changed: another edit won while verification ran. Show “This account changed. Reload it before saving again.” Reload from the list and ask the user to review; never auto-replay or overwrite. Do not expose internal conflict snapshots.
  • 401→Login;403→permission/banned refusal;404→missing/deleted/wrong-organization account, refresh context;422 field errors;429 respect Retry-After. Connect+Edit+Check+Disconnect share10 attempts/min/user across organizations; never auto-retry PATCH.
  • States: loading/current metadata, unchanged form, saving rename, verifying replacement, inline invalid fields, rejected credentials, temporary failure, changed-in-another-window, missing/denied and success.
  • Completed edits appear in account/organization history as cloud_provider.updated; failed/conflicted/no-op saves add no activity. History never contains old/new keys. No undo endpoint: to restore former credentials, user must supply valid prior values again.
  • Done when rename preserves status/secrets, Lightsail replacements require both keys, errors preserve old fields,409 forces reload/review, permission/scope loss is handled, successful lists/history update and no secret leaks. Backend reports736 tests/4252 assertions with quality checks; these are not rerun here. Provider success is still mocked, not verified with real credentials; Pair2 reports this code pulled through cf36220.

#Integrations — Check saved provider credentials (B5.4 built)

Proposed screen: /integrations account row → Check credentials, requires integration-manage (Owner/Admin/granted custom role), active login; unverified users allowed. Pair1 API reference§47: POST /api/v1/organizations/{organization}/cloud-provider-accounts/{cloudProviderAccount}/check, no body/query/input. Namespaces integrations.providers.*, integrations.errors.*, common.*.

  1. User clicks Check → show Checking on that row; do not send new credentials, status, provider or organization fields. This checks saved credentials only; list refresh does not check them.
  2. 200 Connected: display returned translated status, verification completed. Do not promise all future server-create permissions.
  3. 200 Needs new token: check completed and credentials rejected or lacking verification permissions; display Needs new token + Edit. Do not treat200 as a generic green success or422 form error. Successful Edit verifies replacement credentials and restores Connected.
  4. 503 cloud_provider_unavailable: temporary outage/timeout/rate-limit/invalid response; show safe explanation and Retry later. Saved status/fields remain unchanged; do not relabel as revoked.
  5. 503 cloud_provider_credentials_unreadable: saved credentials unusable; show safe returned message + Edit. Do not print raw errors or saved secrets. Status stays unchanged; fresh verified credentials can repair it.
  6. 409 cloud_provider_account_changed: another edit occurred; reload current row before offering a deliberate retry. Never apply the old check result to newly replaced credentials; no automatic POST retry.

Other errors:401→Login;403→banned/permission lost;404→deleted/missing/not-own-organization, refresh context;422 errors.input→client request invalid (remove supplied fields);429→respect Retry-After. Connect/Edit/Check/Disconnect share10 attempts/min/user across organizations. No chosen polling interval: there is no periodic Check, scheduler, automatic discovery or verification on list fetch.

States: idle, checking, connected result, needs-new-token result, temporary failure, saved-credentials unusable, conflict/reload, permission/account unavailable. Only actual status transitions update timestamps/history (cloud_provider.status_changed, safe from/to statuses and actor); unchanged status checks have no new history entry. Failed/unreadable/conflicted checks do not alter history or credentials.

Done when permitted/denied/scope/input cases work;200 both outcomes render distinctly;503 preserves status;unreadable offers Edit;409 cannot overwrite a newer token;no secrets escape;no automatic checks are claimed. Backend reports843 tests/4932 assertions and quality checks; doc sync has not rerun them. Provider successes remain mocked, genuine credential success unverified. Pair2 reports this code pulled through cf36220.

#Integrations — Disconnect provider account (B5.5 partial)

Proposed UI: organization /integrations account row → Disconnect CONFIRM; integration-manage only (Owner/Admin/granted custom role), active login, unverified users allowed. Namespace integrations.providers.*, integrations.errors.*, common.*. Pair1 API reference§48: DELETE /api/v1/organizations/{organization}/cloud-provider-accounts/{cloudProviderAccount}, no input. UI confirmation is a safeguard, not a backend typed-name/password field; never send these.

  • Confirm names the account/provider and explains: removes this saved account/credentials from Central only; cloud-provider token stays valid, no upstream account/resource/server is deleted. Reconnect needs credentials again and gets a new account ID; no Undo.
  • Submit→Disconnecting→200 translated result→remove row and refresh list. Returned data is a deleted metadata snapshot, even if its old status says Connected; do not reinsert it into the list.
  • Other provider accounts and prior organization history remain. One safe cloud_provider.disconnected activity records actor/name/provider; credentials never appear. -401→Login;403→banned/current permission refusal;404→missing/deleted/nonmember/wrong organization or repeat removal, refresh and inform rather than repeated destructive retries;422 errors.input→bad client request, not a confirmation mismatch;429→respect Retry-After. All four mutations share10/min/user across organizations; no automatic DELETE replay.
  • Cancel confirmation leaves account unchanged. On failure keep current row until refreshed; do not assume deletion. Older Edit/Check views must handle404 after disconnect rather than recreating the account or publishing old status.
  • States: confirmation/cancel, removing, success/list, already removed/refreshed, denied, malformed request and retryable error. No secret display or provider verification request needed.
  • Done when allowed/denied/scoped and stale dialogs work,200 snapshot removes row,repeat404 is clear,history preserved,other accounts unaffected and reconnect requirements explicit. Full readiness additionally requires the Phase8 usage blocker and server-name warning. Backend reports910 tests/5607 assertions and quality checks for endpoint only; no claim the missing guard was tested. Pair2 reports this endpoint pulled through cf36220; missing usage guard remains Phase8.

#B. Create a server on a cloud provider — wizard + live progress (moved to the Servers phase)

The backend's draft describes one flow (steps 1–7). The frontend part is the wizard and the progress screen.

Wizard fields (all values come from the API — never typed into the frontend): provider account · region · size · Ubuntu version (22.04 / 24.04 / 26.04 — settled by backend 8.7 on 2026-10-07; MongoDB is not available on 26.04, so MERN cannot use it) · stack (Nginx + PHP · Apache + PHP · Nginx + Node (MERN) · OpenLiteSpeed + PHP) · server name · optional SSH key · root password for Linode (same as V7). Review step before the final button.

Progress screen — the backend's statuses are the only states the frontend shows:

  • Poll the server record while the status is Creating or Installing OSS; stop when it is Ready or Failed.
  • Ready → go to the server page. Failed → show the reason the backend gives, plus Retry and Delete the server at the provider (the second is destructive: confirm dialog naming the server).
  • Installing OSS can take a while (the backend's limit is around 30 minutes), so the screen must be safe to leave and come back to: show the same progress from the server record, and tell the user they can leave.
  • The IP appears only after the provider reports it (step 4) — show "waiting for the IP" instead of an empty field.
  • Two more things the backend parked with this flow: V7 servers on Ubuntu 20.04 can't run OSS (22.04+ only), and Hostinger servers come with Apache/MySQL pre-installed (V7 removed them first) — both belong to the Servers phase, not here.

#C. Git accounts & backup storage (stored on the servers)

Two ideas run through this whole section:

  1. The account is the thing the user manages, and a server is just somewhere it is installed. So the list groups by account ("used on: Server 1, Server 2"), not by server.
  2. Central keeps only a note — name, provider and which servers have it. No secret, ever. So the UI never offers "view token", and every action that needs the secret asks for it again.
#FeatureScreen / controlRulesFrontend notes
5.6Connect on a serverServer → Git (or Storage) → Connect, as a modal in CentralGive the account a name, then paste the token / keys V8 requirement 2026-10-03. The form is in Central because OSS never returns a saved secretAfter saving, the field is gone for good — show only the account name afterwards, with "Replace token" instead of an edit field
5.7Also add to other serversCheckbox list inside the same modal: other servers / all serversOnly servers of the organization the user may manageDefault to just this server. Show how many servers are selected. Servers the user can't manage are not listed at all
5.8Result per serverResult list per serverDone / offline → Retry — for connect, update and remove alike V8 requirement 2026-10-03. The token is saved in OSS, not in CentralOne result table reused by all three actions: server · result · Retry. Partial success is normal, so never show a single "saved" toast for a multi-server action — including removals
5.9TestTest button per serverChecks that the account works on that server V8 requirement 2026-10-03Per-server test, not a Central-side test: show the result inline on the row (works / the error) and when it was last tested. This is what the old "no test from Central" note was waiting for
5.10Update everywhere"Replace token" modalThe user enters the new token once; Central updates all servers that have this accountReuse the per-server result list from 5.8
5.11RemoveConfirm dialog with a choiceThis server only or all serversTwo clearly-labelled buttons, not a dropdown; the "all servers" option lists the servers
5.12New server laterPrompt on a newly-ready serverCentral offers "Connect your accounts here too?" — the user pastes the token again (Central kept no secret)Explain why it's asked again, in one line, or users will think it's a bug
5.14Organization → Integrations pageOne overview pageCloud providers plus all Git and storage accounts, each with "Used on: Server 1, Server 2". Actions: add to more servers, update on all, remove from allThis is the main Integrations page: section A on top (Central-stored), then Git and Storage grouped by account, not by server
5.13Accounts already on the serverSame list, marked as foundIf the OSS panel already has Git or storage accounts, Central reads them from OSS and shows them. The same account on several servers is shown once — even if each server holds a different token ("GitHub jayshree — used on: Server 1, Server 2") V8 requirement 2026-10-03. To add it to more servers, the user pastes the token onceTwo cases in one list: accounts Central set up, and accounts it found on a panel. Mark the found ones ("found on this server") and never pretend Central holds their secret. Grouping is by account, so the same name on two servers must not appear twice. Because the tokens behind one row can differ, the row must not imply one shared secret — see D-30 about what "update everywhere" then does. Matching (provider + account name) comes from the API; the frontend only displays what it is given
5.16Activity logNothing to build here — the entries show up on the organization Activity logEvery integration action is recorded with who, which account, which servers and when: connect, edit, add to other servers, update everywhere, remove, disconnect. Tokens and keys are never logged V8 requirement 2026-10-03The frontend only renders these entries (see Audit log), but it must match the backend's wording: an entry can cover several servers, so show the server list inside one entry instead of one entry per server. Never display a secret even if one ever appeared in a payload
5.15Providers offeredPickersGit: GitHub, GitLab, Bitbucket (access token). Storage: S3 (Amazon S3, Wasabi, custom S3), Google Drive (service account), FTP, SFTPA self-hosted GitLab needs a URL field (V7 had it; the backend's short list no longer says so — worth confirming). S3-compatible needs endpoint + bucket + region. Field sets differ per provider, so drive them from one small config

#Not in this phase (kept as our record)

  • Regions & sizes of a provider (read live when creating a server) → Servers phase (moved out 2026-10-03).
  • Repos & branches (account → repository → branch pickers, read from the server's OSS) → Applications phase (moved out 2026-10-03). The dependent-dropdown spec moves with it.
  • Creating a server on a cloud provider and the automatic OSS install → Servers phase (moved out on 2026-10-03).
  • Connecting an existing / own server (paste the install command) → Servers phase.
  • Server list, server details and server actions → Servers phase.

#Who can do it

Connect, edit, test and remove follow the organization roles from Phase 4 (by Pair 1) V8 requirement 2026-10-03. So every button on these screens is permission-driven: hide what the role can't do, and never show an action that will come back as "not allowed". The exact permission names are still unpublished — build the checks from the API's catalog.

#Existing users

  • Vultr, Hetzner and AWS Lightsail accounts move from V7 as they are — they were already key-based. V8 requirement 2026-10-03
  • DigitalOcean and Linode used a provider login in V7, so after the move the user pastes an API token once. This answers D-29: those accounts arrive needing a token, which is exactly the "Needs new token" state (5.4) — so the first login must show them clearly, with Edit, and explain why in one line.
  • V7's Git and Google Drive connections used one-click login; in V8 they become a pasted token / service account stored in OSS — accepted (Q9, 2026-10-03). How they move is decided later, with the migration phase (Q10), so the screens must cope with "no accounts yet" for a migrated user and say why.
  • Dropbox is not supported in Phase 5 (Q11, skipped for now). V7 users who back up to Dropbox need a plain message and another destination in the storage picker.
  • (The Ubuntu 20.04 limit moved to the Servers phase with the create-server flow.)

#Open points the frontend is waiting on

IDQuestionWhy the frontend cares
Q9One-click login → token / service account — answered 2026-10-03: accepted. Git uses an access token or app password, Google Drive a service account, as in OSSNo OAuth button anywhere in the integration screens. Every connect form is a token form
Q10Moving V7 Git / storage connections — parked: decided later, with the migration phaseMigrated users may arrive with nothing connected, so the empty state must explain that they need to reconnect
Q11Dropbox — skipped for now (2026-10-03): not supported in Phase 5Not in the storage picker. V7 Dropbox users need a message and an alternative
Q12Hostinger — answered 2026-10-03: no. It was never a connectable provider in V7 (only IP detection when adding an existing server, then removing its pre-installed Apache/MySQL). It belongs to the Servers phaseNothing to add to the provider picker
Q13OSS registration is open after an automatic install until someone registersA security hole in the create-server flow, not a screen — now a Servers phase point. If it stays, the progress screen should not show the panel URL before Central has claimed the admin account
Q14Should Central rotate the Central token after the first successful call (it sits in the provider's start-up script)?Possibly a "key rotated" state on the server record — now a Servers phase point. No screen yet
D-27Are integrations account level (Bhavik) or organization level (backend Phase 5)?Decides who owns a provider account and whose servers appear in the "add to other servers" list. The frontend keeps account level for now

Provider code availability (2026-10-10): Pair2 reports B5.1–B5.5 pulled through cf36220;1211 tests/7473 assertions, Pint/PHPStan and optimization passed after pull (not rerun by this sync). List/Connect/Edit/Check and partial Disconnect are present in its copy. No genuine provider success is established. Disconnect server-usage blocker/server-name warning remain unbuilt until Phase8; Git/storage still wait Servers.

ServerAvatar V8 Central · prepared by central-app-2 (Pair 2 frontend) for Bhavik Jethwa · the frontend in this spec is not built yet; the backend started on 2026-10-08 · Built 2026-10-29 03:20 UTC