Files
2026-04-01 15:49:15 +02:00

31 KiB

Phase 13: Add Remaining RClone Remotes - Research

Researched: 2026-04-01 Domain: rclone backend registry expansion, React component architecture, UX search/categorization Confidence: HIGH


<user_constraints>

User Constraints (from CONTEXT.md)

Locked Decisions

  • Comprehensive coverage — all rclone backends that make sense for unattended Windows deployment
  • OAuth-requiring backends included via token-paste approach (same as OneDrive: user runs rclone authorize locally, pastes resulting JSON token)
  • All new backends shipped in a single phase (no sub-phases by auth type)
  • Claude picks the specific backend list based on rclone documentation and deployment viability
  • Reuse per-backend AuthToggle component pattern for backends with mutually-exclusive auth fields (e.g., FTP password vs key file) — same proven pattern as AzureAuthToggle/SftpAuthToggle
  • Build a new reusable OAuthInstructions component for OAuth-token backends — collapsible step-by-step guide showing how to run rclone authorize for that specific backend
  • OAuthInstructions takes backend-specific command and steps as props, keeps RemoteConfigStep clean
  • Categorized sections with headings: Cloud Object Storage, Cloud Drives, Protocol-based — grouped by storage type
  • Search/filter bar at the top that filters instantly as user types
  • Search matches across all fields: displayName, description, category, and field labels
  • Categories with no matching backends collapse when search is active
  • Add category field to each BACKEND_REGISTRY entry — single source of truth stays single
  • Add inline SVG icons per backend card — hand-picked SVGs bundled in the app, no external dependency

Claude's Discretion

  • BackendType implementation: keep explicit union type or derive dynamically from registry keys — pick whichever balances type safety and maintainability best at 20+ backends
  • Specific backend list selection based on rclone docs and unattended deployment viability
  • Field validation depth per backend (regex patterns vs simple required/optional)
  • Icon design approach (monochrome vs brand colors, sizing)

Deferred Ideas (OUT OF SCOPE)

None — discussion stayed within phase scope </user_constraints>


Summary

Phase 13 expands the BACKEND_REGISTRY from 7 to approximately 18-20 entries, grouping them into three categories (Cloud Object Storage, Cloud Drives, Protocol-based). Each new backend requires: a registry entry, a RCLONE_TYPE_MAP entry, a buildZodSchema() call, and a backendLabel entry. Backends with OAuth tokens get an OAuthInstructions collapsible component. Backends with mutually-exclusive auth get a new AuthToggle component. The BackendSelectionStep gets a complete UX overhaul with category headings, instant search, and inline SVG icons.

The critical architectural decision is whether BackendType stays as an explicit union literal or is derived from registry keys. At 20+ backends, explicit union becomes a maintenance burden (every new backend requires editing 6 files). Deriving from registry keys reduces that to 4 files and eliminates the "forgot to update BackendType" class of bug.

Primary recommendation: Derive BackendType from keyof typeof BACKEND_REGISTRY to reduce per-backend boilerplate from 6 touch-points to 4. Use as const assertion on the registry object for full TypeScript narrowing.


Based on rclone documentation and suitability for unattended Windows deployment via RMM/Intune, the following backends should be added. Each entry shows the rclone type string and auth approach.

Category: Cloud Object Storage (S3-like)

These all use static API credentials — ideal for unattended deployment.

Backend ID displayName rclone type Auth approach Notes
azureblob Azure Blob Storage azureblob Key or SAS URL Existing
azure-files Azure Files Storage azurefiles Account + Key New — uses account + key fields
s3 Amazon S3 s3 Access Key + Secret Existing
s3-compatible S3-Compatible s3 (provider=Other) Access Key + Secret + Endpoint Existing
gcs Google Cloud Storage google cloud storage Service Account JSON Existing
b2 Backblaze B2 b2 Application Key ID + Key Existing
swift OpenStack Swift swift User + Key + Auth URL + Tenant New — API credential

Category: Cloud Drives (OAuth token-paste)

These require OAuth — user runs rclone authorize locally, pastes JSON token.

Backend ID displayName rclone type Auth approach Notes
onedrive OneDrive onedrive OAuth token + drive_id + drive_type Existing
gdrive Google Drive drive OAuth token + service account option New — token or service_account_credentials
dropbox Dropbox dropbox OAuth token (JSON blob) New — token field only
box Box box OAuth token (JSON blob) New — token + box_sub_type
pcloud pCloud pcloud OAuth token (JSON blob) New — token + hostname

Category: Protocol-based

These use server credentials — IP/hostname, user, password.

Backend ID displayName rclone type Auth approach Notes
sftp SFTP sftp Password or Key PEM (AuthToggle) Existing
ftp FTP ftp Host + User + Password New — also FTPS via tls select
webdav WebDAV webdav URL + User + Password + Vendor select New — includes NextCloud, SharePoint, OwnCloud, etc.
smb SMB / Windows Share smb Host + User + Pass + Domain New — ideal for Windows shops
http HTTP (read-only) http URL only New — simple, read-only
seafile Seafile seafile URL + User + Password New — self-hosted cloud

Total: 7 existing + 10 new = 17 backends

Excluded from scope (not suitable for unattended Windows deployment):

  • HDFS — Hadoop/enterprise Linux ecosystem, not Windows-native
  • Google Photos — read-only, consumer
  • iCloud Drive — macOS-only tooling
  • Mega, Jottacloud, Yandex, Mail.ru — consumer-grade, poor enterprise adoption
  • pCloud — included only if token-paste approach is feasible (MEDIUM confidence)
  • Internet Archive, Pixeldrain, Uloz.to, Gofile — public/consumer file sharing

Standard Stack

Core (no new dependencies)

Library Version Purpose Why Standard
react ^18.3.1 UI rendering Existing
react-hook-form ^7.72.0 Form state + validation Existing — all forms use it
zod ^4.3.6 Schema validation Existing — buildZodSchema() auto-derives
@hookform/resolvers ^5.2.2 Zod <-> RHF bridge Existing

No new npm dependencies. Inline SVG icons are bundled as TSX components. No icon library needed.

Installation

# Nothing new to install — all new code uses existing stack

Architecture Patterns

src/
├── schemas/
│   └── registry.ts          # Add ~10 new backend entries + category field
├── generators/
│   └── rclone-conf.ts       # Add ~10 entries to RCLONE_TYPE_MAP
├── components/
│   ├── wizard/
│   │   ├── BackendSelectionStep.tsx   # Full overhaul: categories, search, icons
│   │   ├── RemoteConfigStep.tsx       # Add auth-toggle branches for FTP/WebDAV
│   │   ├── FtpAuthToggle.tsx          # New: password vs no-auth (anonymous) toggle
│   │   ├── OAuthInstructions.tsx      # New: reusable collapsible rclone authorize guide
│   │   └── [backend]AuthToggle.tsx    # Only if backend needs mutually-exclusive auth
│   └── icons/
│       └── BackendIcons.tsx           # New: inline SVG icon map, keyed by BackendType

The current explicit union 'azureblob' | 's3' | ... must be touched in registry.ts every time a backend is added. At 17+ backends, derive it instead:

// src/schemas/registry.ts
export const BACKEND_REGISTRY = {
  azureblob: { ... },
  gdrive: { ... },
  // ... all entries
} as const;  // <-- CRITICAL: 'as const' needed for keyof narrowing

export type BackendType = keyof typeof BACKEND_REGISTRY;
// Result: 'azureblob' | 's3' | 'gdrive' | ...  (auto-updated when registry grows)

What changes: BackendType no longer lives in registry.ts as a separate literal union — it is derived. The src/store/types.ts re-export (export type { BackendType }) continues to work unchanged. All consumers importing BackendType from ./registry or ../store/types require zero changes.

What must be verified: The BACKEND_REGISTRY cast at the bottom currently uses as Record<BackendType, ...> which would become circular. Remove that cast — with as const the registry is already fully typed by inference.

Confidence: HIGH — standard TypeScript pattern, verified against codebase.

Pattern 2: BACKEND_REGISTRY Entry with category Field

Add category to the registry entry — single source of truth for categorization:

// src/schemas/registry.ts
export type BackendCategory = 'cloud-object-storage' | 'cloud-drives' | 'protocol-based';

export interface BackendMeta {
  displayName: string;
  description: string;
  category: BackendCategory;  // NEW field
  fields: FieldDef[];
}

export const BACKEND_REGISTRY = {
  azureblob: {
    displayName: 'Azure Blob Storage',
    description: 'Microsoft Azure cloud storage',
    category: 'cloud-object-storage',  // NEW
    fields: [ ... ],
  },
  gdrive: {
    displayName: 'Google Drive',
    description: 'Google Drive (paste pre-obtained rclone token)',
    category: 'cloud-drives',
    fields: [ ... ],
  },
} as const;

Pattern 3: OAuthInstructions Component

Collapsible component, collapsed by default. Takes backend-specific props:

// src/components/wizard/OAuthInstructions.tsx
interface OAuthInstructionsProps {
  backendName: string;       // e.g. "Google Drive"
  authorizeCommand: string;  // e.g. 'rclone authorize "drive"'
  steps?: string[];          // optional custom step descriptions
}

export function OAuthInstructions({ backendName, authorizeCommand, steps }: OAuthInstructionsProps) {
  const [expanded, setExpanded] = useState(false);
  // Renders a collapsible details/summary or button-controlled div
  // Shows numbered steps: install rclone → run command → browser opens → paste JSON
}

Used in RemoteConfigStep for gdrive, dropbox, box, pcloud alongside the token PasswordField:

// In RemoteConfigStep, new OAuth branch:
} : backendType === 'gdrive' ? (
  <>
    <OAuthInstructions
      backendName="Google Drive"
      authorizeCommand='rclone authorize "drive"'
    />
    <FieldRenderer field={BACKEND_REGISTRY.gdrive.fields.find(f => f.key === 'token')!} ... />
    <FieldRenderer field={BACKEND_REGISTRY.gdrive.fields.find(f => f.key === 'root_folder_id')!} ... />
  </>
)

The current flat grid becomes a category-grouped, searchable layout:

// Category display order (stable, not alphabetical)
const CATEGORY_ORDER: BackendCategory[] = [
  'cloud-object-storage',
  'cloud-drives',
  'protocol-based',
];

const CATEGORY_LABELS: Record<BackendCategory, string> = {
  'cloud-object-storage': 'Cloud Object Storage',
  'cloud-drives': 'Cloud Drives',
  'protocol-based': 'Protocol-based',
};

// Search filter logic — matches displayName, description, category label, and field labels
function matchesSearch(entry: BackendMeta, query: string): boolean {
  const q = query.toLowerCase();
  if (entry.displayName.toLowerCase().includes(q)) return true;
  if (entry.description.toLowerCase().includes(q)) return true;
  if (CATEGORY_LABELS[entry.category].toLowerCase().includes(q)) return true;
  if (entry.fields.some(f => f.label.toLowerCase().includes(q))) return true;
  return false;
}

Search bar uses the existing TextFieldMD3 component. The search query is stored in local useState — no global state needed.

Pattern 5: Inline SVG Icon Component

Icons are TSX components returning <svg> elements directly — no external dependency, full styling control:

// src/components/icons/BackendIcons.tsx
interface IconProps { className?: string; }

export const AzureIcon = ({ className }: IconProps) => (
  <svg className={className} viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
    {/* Azure blob SVG path */}
  </svg>
);

// Icon map keyed by BackendType
export const BACKEND_ICONS: Partial<Record<BackendType, React.FC<IconProps>>> = {
  azureblob: AzureIcon,
  gdrive: GoogleDriveIcon,
  // ...
};

BackendCard receives an optional icon prop rendered at the top-left of the card.

Anti-Patterns to Avoid

  • Maintaining explicit BackendType union: At 17+ backends, a forgotten update causes silent TypeScript errors in backendLabel exhaustiveness checks. Derive from registry keys instead.
  • Splitting category into a separate map: Keeping it in BACKEND_REGISTRY means one file to edit per new backend. A separate BACKEND_CATEGORIES record creates two sources of truth.
  • Using display: none CSS for search-hidden categories: The category heading itself must be hidden when its backends are all filtered out — use conditional rendering, not CSS hiding (CSS hiding leaves empty heading wrappers in DOM).
  • Eager OAuthInstructions expansion: Default expanded state wastes screen space and is intimidating. Default collapsed, user expands when they need help.
  • Placeholder prop on TextFieldMD3: TextFieldMD3 uses placeholder=" " (space) internally for the CSS-only floating label. Do NOT pass a real placeholder string — it will break the peer-[:not(:placeholder-shown)] selector. Use helpText for descriptive hints instead.

Don't Hand-Roll

Problem Don't Build Use Instead Why
OAuth token paste textarea Multi-line textarea component PasswordField (inputType: 'password') Existing component with show/hide, tooltip, validation — textarea breaks form validation contract
Search debouncing useDebounce hook Direct useState + filter on render 17 backends filter in <1ms; debounce is premature optimization
Icon library npm icon library install Inline TSX SVG components in BackendIcons.tsx Zero dependency, full Tailwind/CSS control, no bundle bloat
BackendType exhaustiveness check Manual assertNever() keyof typeof BACKEND_REGISTRY derivation TypeScript already errors if a key is missing from backendLabel record
Category data store Separate CATEGORY_REGISTRY map category field on BACKEND_REGISTRY entry Single source of truth already established
rclone type mapping override Per-backend conditional logic RCLONE_TYPE_MAP entry (existing pattern) Already used for s3-compatibles3

Key insight: The registry-driven pattern already handles 95% of new-backend complexity. Most new backends (FTP, Seafile, Koofr) need zero custom React components — just a registry entry, a RCLONE_TYPE_MAP entry, a schema call, and a backendLabel string.


Common Pitfalls

Pitfall 1: BackendType Cast Conflicts with as const

What goes wrong: The current registry.ts bottom has } as Record<BackendType, { displayName: string; description: string; fields: FieldDef[] }>. When BackendType is derived from keyof typeof BACKEND_REGISTRY, this cast creates a circular dependency (BackendType depends on the registry type, which is cast to use BackendType).

Why it happens: TypeScript cannot resolve the type when both sides of the as cast reference each other.

How to avoid: Remove the cast entirely. With as const on the registry object, TypeScript infers the full precise type. The BackendMeta interface should be used for individual entries, not as a cast on the whole object.

Warning signs: TypeScript error like "Type alias 'BackendType' circularly references itself."

Pitfall 2: backendLabel Record in RemoteConfigStep Breaks Exhaustiveness

What goes wrong: backendLabel is typed as Record<NonNullable<typeof backendType>, string> — adding a new BackendType without adding to backendLabel causes a TypeScript error at this record. This is desirable (forced exhaustiveness) but easy to miss during development.

How to avoid: Add backendLabel entry as the FIRST thing when adding a new backend. Use displayName from registry directly instead of duplicating: const backendLabel = Object.fromEntries(Object.entries(BACKEND_REGISTRY).map(([k, v]) => [k, v.displayName])) — eliminates the duplication entirely.

Warning signs: TS2741 "Property X is missing in type Record<...>"

Pitfall 3: BACKEND_SCHEMAS Explicit Object Misses New Backends

What goes wrong: BACKEND_SCHEMAS in src/schemas/index.ts is an explicit object with one buildZodSchema() call per backend. Adding a backend to the registry but forgetting BACKEND_SCHEMAS causes a runtime crash in RemoteConfigStep when the schema lookup returns undefined.

How to avoid: Auto-generate BACKEND_SCHEMAS from the registry:

export const BACKEND_SCHEMAS = Object.fromEntries(
  (Object.keys(BACKEND_REGISTRY) as BackendType[]).map(t => [t, buildZodSchema(t)])
) as Record<BackendType, ReturnType<typeof buildZodSchema>>;

This makes BACKEND_SCHEMAS auto-expand whenever the registry grows.

Warning signs: Runtime error "Cannot read properties of undefined (reading 'parse')" in RemoteConfigStep.

Pitfall 4: FTP/WebDAV Toggle Auth — Zod Schema for Hidden Fields

What goes wrong: AzureAuthToggle and SftpAuthToggle register BOTH auth fields always (CSS-hide the inactive one) so values are preserved on tab switch. The Zod schema marks both as optional. This works for Azure (key/sas_url are both optional) but FTP's pass field may be expected to be required — making it optional in Zod allows submitting with no password, which silently creates a broken config.

Why it happens: Toggle pattern requires both fields to be optional in Zod regardless of visual state.

How to avoid: For FTP, both pass (required: false) and the anonymous-access toggle approach is fine since FTP often legitimately uses anonymous auth. Mark both toggle fields as required: false in the registry. Document in helpText that the inactive field is ignored.

Pitfall 5: Search Filter Leaves Empty Category Headings in DOM

What goes wrong: If search is implemented with CSS hidden on non-matching cards but the category heading is always rendered, an empty <h3>Cloud Drives</h3> appears when all Cloud Drive backends are filtered out.

How to avoid: Filter backends per category before render, skip the entire category block (heading + cards) when filteredBackends.length === 0 for that category.

Pitfall 6: rclone Type Strings Are Exact and Case-Sensitive

What goes wrong: RCLONE_TYPE_MAP maps backend IDs to rclone type strings. Errors here produce a broken rclone.conf that rclone silently rejects.

Verified mappings (HIGH confidence — from rclone official docs):

BackendType rclone type string
gdrive drive
dropbox dropbox
box box
pcloud pcloud
ftp ftp
webdav webdav
smb smb
http http
swift swift
seafile seafile
azure-files azurefiles

Warning signs: rclone error "didn't find section in config file" or "unknown backend type."

Pitfall 7: Google Drive Has Two Auth Paths — Service Account vs OAuth Token

What goes wrong: Google Drive supports both service account JSON (for unattended, like GCS) and OAuth token paste (like OneDrive). Exposing both options requires an AuthToggle, or a decision to support only one path.

Recommendation: Support both via a GdriveAuthToggle component:

  • Tab 1: "OAuth Token" — token field + OAuthInstructions component
  • Tab 2: "Service Account" — service_account_credentials field (reuses GCS pattern)

This matches the unattended deployment goal: domain admins can use service accounts; personal Drive setups use OAuth token paste.


Code Examples

Verified patterns from codebase:

Auto-derived BackendType (replaces explicit union)

// src/schemas/registry.ts
export const BACKEND_REGISTRY = {
  azureblob: { displayName: '...', description: '...', category: 'cloud-object-storage', fields: [...] },
  gdrive:    { displayName: '...', description: '...', category: 'cloud-drives', fields: [...] },
  // ...
} as const;  // REQUIRED for keyof narrowing

export type BackendCategory = 'cloud-object-storage' | 'cloud-drives' | 'protocol-based';
export type BackendType = keyof typeof BACKEND_REGISTRY;
// Automatically includes every key in the registry

Auto-generated BACKEND_SCHEMAS (replaces explicit per-backend calls)

// src/schemas/index.ts
export const BACKEND_SCHEMAS = Object.fromEntries(
  (Object.keys(BACKEND_REGISTRY) as BackendType[]).map(t => [t, buildZodSchema(t)])
) as Record<BackendType, ReturnType<typeof buildZodSchema>>;

backendLabel derived from registry (removes duplicate displayNames)

// In RemoteConfigStep.tsx
const backendLabel = Object.fromEntries(
  Object.entries(BACKEND_REGISTRY).map(([k, v]) => [k, v.displayName])
) as Record<BackendType, string>;

Category-grouped backend rendering in BackendSelectionStep

const CATEGORY_ORDER: BackendCategory[] = ['cloud-object-storage', 'cloud-drives', 'protocol-based'];

// Inside component:
const filteredEntries = Object.entries(BACKEND_REGISTRY).filter(
  ([, entry]) => !query || matchesSearch(entry, query)
);

{CATEGORY_ORDER.map(cat => {
  const backends = filteredEntries.filter(([, e]) => e.category === cat);
  if (backends.length === 0) return null;  // collapse empty categories
  return (
    <section key={cat}>
      <h3>{CATEGORY_LABELS[cat]}</h3>
      <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
        {backends.map(([type, entry]) => (
          <BackendCard key={type} ... icon={BACKEND_ICONS[type as BackendType]} />
        ))}
      </div>
    </section>
  );
})}

OAuthInstructions usage in RemoteConfigStep

// For gdrive OAuth path in RemoteConfigStep:
<OAuthInstructions
  backendName="Google Drive"
  authorizeCommand='rclone authorize "drive"'
/>
<FieldRenderer
  field={BACKEND_REGISTRY.gdrive.fields.find(f => f.key === 'token')!}
  register={register}
  error={errors.token as FieldError | undefined}
/>

FTP registry entry (example of new backend with optional FTPS)

ftp: {
  displayName: 'FTP',
  description: 'File Transfer Protocol (supports FTPS)',
  category: 'protocol-based',
  fields: [
    { key: 'host',          label: 'Host',     inputType: 'text',     required: true,  placeholder: 'ftp.example.com' },
    { key: 'user',          label: 'Username', inputType: 'text',     required: false, placeholder: 'anonymous', helpText: 'Leave blank for anonymous FTP' },
    { key: 'pass',          label: 'Password', inputType: 'password', required: false },
    { key: 'port',          label: 'Port',     inputType: 'text',     required: false, placeholder: '21' },
    { key: 'explicit_tls',  label: 'TLS Mode', inputType: 'select',   required: false,
      options: [
        { value: '',     label: 'Plain FTP (no encryption)' },
        { value: 'true', label: 'Explicit FTPS (STARTTLS)' },
      ],
      helpText: 'Use Explicit FTPS for encrypted FTP connections (port 21 + STARTTLS)',
    },
  ],
},

State of the Art

Old Approach Current Approach When Changed Impact
Explicit BackendType union literal (7 entries) Derive from keyof typeof BACKEND_REGISTRY Phase 13 Eliminates one of 6 touch-points per new backend
Explicit per-backend buildZodSchema() calls Auto-generate from registry keys Phase 13 Eliminates one of 6 touch-points per new backend
Manual backendLabel record duplication Derive from entry.displayName Phase 13 Eliminates last duplication of displayName
Flat card grid (no categories) Category-grouped with search Phase 13 Required UX for 17+ backends
No OAuth guidance OAuthInstructions collapsible component Phase 13 New backends (gdrive, dropbox, box) need this

Validation Architecture

Test Framework

Property Value
Framework Vitest ^4.1.1
Config file vite.config.ts (test.environment: jsdom) + vitest.config.ts (node env for generator tests)
Quick run command npx vitest run
Full suite command npx vitest run

Phase Requirements → Test Map

Behavior Test Type Automated Command File Exists?
All 17 backend types present in BACKEND_REGISTRY unit npx vitest run src/schemas/registry.test.ts Update existing
Each new backend has required fields unit npx vitest run src/schemas/registry.test.ts Update existing
BACKEND_SCHEMAS auto-covers new backends (no undefined) unit npx vitest run src/schemas/index.test.ts Wave 0 gap
RCLONE_TYPE_MAP has entry for each new backend unit npx vitest run src/generators/rclone-conf.test.ts Update existing
BackendSelectionStep renders all 17 cards unit npx vitest run src/components/wizard/BackendSelectionStep.test.tsx Update existing
Search filter hides non-matching cards unit npx vitest run src/components/wizard/BackendSelectionStep.test.tsx Wave 0 gap
Category headings collapse when all items filtered unit npx vitest run src/components/wizard/BackendSelectionStep.test.tsx Wave 0 gap
OAuthInstructions renders collapsed by default unit npx vitest run src/components/wizard/OAuthInstructions.test.tsx Wave 0 gap
OAuthInstructions expands on button click unit npx vitest run src/components/wizard/OAuthInstructions.test.tsx Wave 0 gap
RemoteConfigStep renders gdrive form fields unit npx vitest run src/components/wizard/RemoteConfigStep.test.tsx Update existing
RemoteConfigStep renders ftp form fields unit npx vitest run src/components/wizard/RemoteConfigStep.test.tsx Update existing
buildRcloneConf outputs correct type for gdrive unit npx vitest run src/generators/rclone-conf.test.ts Update existing

Sampling Rate

  • Per task commit: npx vitest run
  • Per wave merge: npx vitest run
  • Phase gate: Full suite green before /gsd:verify-work

Wave 0 Gaps

  • src/schemas/index.test.ts — covers BACKEND_SCHEMAS auto-generation (verify no undefined for new types)
  • src/components/wizard/OAuthInstructions.test.tsx — covers collapsed default + expand behavior
  • New test cases in BackendSelectionStep.test.tsx — search filter and category collapse behavior

Open Questions

  1. Google Drive: OAuth token vs service account — single form or toggle?

    • What we know: GCS already uses service_account_credentials. Google Drive supports both approaches.
    • What's unclear: Whether IT pros deploying via RMM will have service accounts for Drive (less common than for GCS).
    • Recommendation: Implement GdriveAuthToggle with two tabs (OAuth token + Service Account) to cover both. If that adds too much complexity, default to OAuth token path only (simpler, more universally applicable).
  2. Azure Files vs Azure Blob — same azurefiles type string?

    • What we know: rclone docs list azurefiles as the type for Azure Files Storage (distinct from azureblob).
    • What's unclear: Required fields for azurefiles — need to verify account/key vs connection_string approach.
    • Recommendation: Verify against https://rclone.org/azurefiles/ during plan execution before writing registry fields. HIGH confidence on type string, MEDIUM confidence on exact field names.
  3. pCloud hostname requirement for EU users

    • What we know: pCloud has region servers; EU users must set hostname to eapi.pcloud.com.
    • What's unclear: Can we default hostname to api.pcloud.com and let users override, or is a select dropdown better?
    • Recommendation: Add hostname as a select with two options (US/EU) — clearer than asking IT pros to know the API hostname.
  4. BackendCard icon sizing and color

    • What we know: User decided "inline SVG, no npm library."
    • What's unclear: Monochrome (currentColor, works in dark mode) vs brand colors.
    • Recommendation: Monochrome (fill="currentColor") for consistency with MD3 token system and dark mode compatibility. Brand-colored icons require hardcoded hex values that ignore the MD3 color token system.

Sources

Primary (HIGH confidence)

  • rclone.org/drive/ — Google Drive type string (drive), service account fields, OAuth token structure
  • rclone.org/dropbox/ — Dropbox type string (dropbox), token field
  • rclone.org/box/ — Box type string (box), token + box_sub_type fields
  • rclone.org/pcloud/ — pCloud type string (pcloud), token + hostname fields
  • rclone.org/ftp/ — FTP type string (ftp), host/user/pass/tls fields
  • rclone.org/webdav/ — WebDAV type string (webdav), url/user/pass/vendor fields + vendor options
  • rclone.org/smb/ — SMB type string (smb), host/user/pass/domain fields
  • rclone.org/swift/ — OpenStack Swift type string (swift), required fields
  • rclone.org/seafile/ — Seafile type string (seafile), url/user/pass fields
  • rclone.org/koofr/ — Koofr type string (koofr), user/password fields
  • rclone.org/s3/ — S3-compatible provider enum (Wasabi, Cloudflare, MinIO, etc.)
  • Existing codebase (registry.ts, BackendSelectionStep.tsx, RemoteConfigStep.tsx) — HIGH confidence on patterns

Secondary (MEDIUM confidence)

  • rclone.org/overview/ — full backend list confirmed
  • rclone.org/docs/ — comprehensive backend list verified

Tertiary (LOW confidence)


Metadata

Confidence breakdown:

  • Standard stack: HIGH — no new dependencies, all existing patterns
  • Backend list selection: HIGH — rclone official docs confirmed for all 10 new backends
  • rclone type strings: HIGH — verified against official docs for all backends
  • Architecture patterns (BackendType derivation): HIGH — standard TypeScript keyof typeof pattern
  • Architecture patterns (OAuthInstructions component): HIGH — directly mirrors existing AzureAuthToggle/SftpAuthToggle patterns
  • BackendSelectionStep overhaul: HIGH — filter/group/search is standard React pattern, no novel abstractions
  • Azure Files field names: MEDIUM — type string confirmed, credential fields need docs verification
  • pCloud EU hostname handling: MEDIUM — documented in rclone docs, implementation approach is recommendation

Research date: 2026-04-01 Valid until: 2026-05-01 (rclone docs are stable; backend type strings rarely change)