> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kombify.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up your password manager apps

> Create your vault account, save a first password, and connect the Bitwarden apps on your phone, browser and computer to your own vault.

export const ClientSetupGuide = ({id, meta = [], requirements = [], requirementsHelp, appsTitle = 'First, get the app.', vendor, apps = [], appsNote, stepsTitle = 'Connect step by step.', steps = [], help = [], helpTitle = 'Stuck?', next = [], nextTitle = 'What comes next.', attribution, sources}) => {
  const Anchor = 'a', Article = 'article', Details = 'details', Dialog = 'dialog', Figcaption = 'figcaption', Figure = 'figure', Footer = 'footer', Img = 'img', Summary = 'summary';
  const count = steps.length;
  const last = count - 1;
  const storageKey = `kombify-client-guide:${id}:v1`;
  const rootRef = useRef(null);
  const zoomRef = useRef(null);
  const toastTimer = useRef(null);
  const focusTab = useRef(false);
  const [current, setCurrent] = useState(0);
  const [completed, setCompleted] = useState(() => steps.map(() => false));
  const [verified, setVerified] = useState(() => steps.map(step => (step.verify || []).map(() => false)));
  const [failed, setFailed] = useState({});
  const [zoom, setZoom] = useState(null);
  const [toast, setToast] = useState('');
  const isExternal = href => (/^https?:\/\//).test(href || '');
  const icon = name => {
    const paths = {
      apple: <path d="M16.7 1.8c0 2.1-1.7 4.4-3.7 4.2-.4-2.1 1.8-4.3 3.7-4.2ZM20.4 17.3c-.5 1.2-.8 1.7-1.5 2.7-1 1.5-2.4 3.3-4.2 3.3-1.6 0-2-1-4.1-.9-2.1 0-2.5 1-4.2.9-1.8-.1-3.1-1.7-4.1-3.2C-.5 15.9-.7 10.4 1 7.6c1.2-2 3.1-3.1 4.9-3.1 1.9 0 3.1 1 4.7 1 1.5 0 2.5-1 4.7-1 1.6 0 3.3.9 4.5 2.3-4 2.2-3.3 8 0 10.5Z" fill="currentColor" stroke="none" transform="translate(2 0) scale(.87)" />,
      android: <><path d="M5 12a7 7 0 0 1 14 0v6H5Zm2-6L5 3m12 3 2-3M2 12v5m20-5v5M8 18v4m8-4v4" /><path d="M8.5 9h.01M15.5 9h.01" /></>,
      browser: <><rect height="16" rx="2" width="20" x="2" y="4" /><path d="M2 9h20M6 6.5h.01M9 6.5h.01" /></>,
      desktop: <><rect height="14" rx="2" width="20" x="2" y="3" /><path d="M8 22h8M12 17v5" /></>,
      web: <><circle cx="12" cy="12" r="9" /><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" /></>,
      server: <><rect height="7" rx="2" width="18" x="3" y="3" /><rect height="7" rx="2" width="18" x="3" y="14" /><path d="M7 6.5h.01M7 17.5h.01M16 6.5h2M16 17.5h2" /></>,
      phone: <><rect height="20" rx="3" width="12" x="6" y="2" /><path d="M10 5h4M11 19h2" /></>,
      steps: <><rect height="7" rx="1.5" width="7" x="3" y="3" /><rect height="7" rx="1.5" width="7" x="14" y="3" /><rect height="7" rx="1.5" width="7" x="3" y="14" /><rect height="7" rx="1.5" width="7" x="14" y="14" /></>,
      book: <path d="M12 5v15M3 4c4-1 6 0 9 1 3-1 5-2 9-1v15c-4-1-6 0-9 1-3-1-5-2-9-1Z" />,
      clock: <><circle cx="12" cy="12" r="9" /><path d="M12 7v5l3 2" /></>,
      print: <><path d="M6 8V3h12v5M6 17H3V8h18v9h-3M6 14h12v7H6Z" /><path d="M17 11h.01" /></>,
      check: <path d="m5 12 4 4L19 6" />,
      arrow: <path d="M5 12h14M13 6l6 6-6 6" />,
      back: <path d="m15 5-7 7 7 7" />,
      external: <><path d="M14 4h6v6m0-6L10 14" /><path d="M10 4H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-5" /></>,
      info: <><circle cx="12" cy="12" r="9" /><path d="M12 11v5M12 7h.01" /></>,
      ok: <><circle cx="12" cy="12" r="9" /><path d="m8 12 3 3 5-6" /></>,
      camera: <><path d="M3 7h4l2-3h6l2 3h4v13H3Z" /><circle cx="12" cy="13" r="4" /></>,
      zoom: <><circle cx="10.5" cy="10.5" r="6.5" /><path d="m16 16 5 5M7 10.5h7M10.5 7v7" /></>,
      shield: <><path d="M12 3 3 7v6c0 5 9 9 9 9s9-4 9-9V7Z" /><path d="m8 12 3 3 5-6" /></>,
      close: <path d="m6 6 12 12M6 18 18 6" />
    };
    return <svg aria-hidden="true" className="ico" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.7" viewBox="0 0 24 24">{paths[name] || paths.info}</svg>;
  };
  const safeHref = href => (/^(https:\/\/|\/|#)/).test(href || '') ? href : '#';
  const link = (href, children, className, key) => isExternal(href) ? <Anchor className={className || ''} href={safeHref(href)} key={key} rel="noopener noreferrer" target="_blank">{children} {icon('external')}<span className="sr-only"> (opens in a new tab)</span></Anchor> : <Anchor className={className || ''} href={safeHref(href)} key={key} onClick={(href || '').startsWith('#') ? () => {
    zoomRef.current?.close();
    reveal(href.slice(1));
  } : undefined}>{children}</Anchor>;
  const rich = text => {
    if (!text || !text.split) return text;
    return text.split(/(\[\[[^\]]+\]\]|\*\*[^*]+\*\*|\[[^\]]+\]\([^)\s]+\))/g).map((part, i) => {
      if ((/^\[\[[^\]]+\]\]$/).test(part)) return <span className="key" key={i}>{part.slice(2, -2)}</span>;
      if ((/^\*\*[^*]+\*\*$/).test(part)) return <strong key={i}>{part.slice(2, -2)}</strong>;
      const match = (/^\[([^\]]+)\]\(([^)\s]+)\)$/).exec(part);
      if (match) return link(match[2], match[1], undefined, i);
      return part;
    });
  };
  useEffect(() => {
    try {
      const saved = JSON.parse(localStorage.getItem(storageKey) || 'null');
      if (Array.isArray(saved?.completed) && saved.completed.length === count) setCompleted(saved.completed.map(Boolean));
      if (Array.isArray(saved?.verified) && saved.verified.length === count) setVerified(saved.verified.map((list, i) => (steps[i].verify || []).map((_, j) => Boolean(list?.[j]))));
    } catch (_) {}
    const root = rootRef.current;
    const timer = setTimeout(() => {
      if (!root) return;
      const broken = {};
      root.querySelectorAll('.screen-image > img').forEach(image => {
        if (image.complete && !image.naturalWidth) broken[image.getAttribute('src')] = true;
      });
      if (Object.keys(broken).length) setFailed(old => ({
        ...old,
        ...broken
      }));
    }, 7000);
    const openAll = () => root?.querySelectorAll('details').forEach(node => {
      node.open = true;
    });
    window.addEventListener('beforeprint', openAll);
    return () => {
      clearTimeout(timer);
      clearTimeout(toastTimer.current);
      window.removeEventListener('beforeprint', openAll);
    };
  }, []);
  useEffect(() => {
    if (!focusTab.current) return;
    focusTab.current = false;
    rootRef.current?.querySelector(`[id="${id}-tab-${current}"]`)?.focus();
  }, [current]);
  const persist = (nextCompleted, nextVerified) => {
    try {
      localStorage.setItem(storageKey, JSON.stringify({
        completed: nextCompleted,
        verified: nextVerified
      }));
    } catch (_) {}
  };
  const say = text => {
    setToast('');
    setTimeout(() => setToast(text), 60);
    clearTimeout(toastTimer.current);
    toastTimer.current = setTimeout(() => setToast(''), 3800);
  };
  const select = (index, focus) => {
    const target = Math.max(0, Math.min(last, index));
    setCurrent(target);
    focusTab.current = Boolean(focus);
  };
  const onNext = () => {
    const checks = verified[current] || [];
    if (checks.length && !checks.every(Boolean)) {
      say('Tick each point after you checked it yourself.');
      rootRef.current?.querySelector(`[id="${id}-verify-${current}-${checks.indexOf(false)}"]`)?.focus();
      return;
    }
    const nextCompleted = completed.map((done, i) => i === current ? true : done);
    setCompleted(nextCompleted);
    persist(nextCompleted, verified);
    if (current < last) select(current + 1, true); else {
      const done = nextCompleted.filter(Boolean).length;
      say(done === count ? 'All steps are marked as done in this browser.' : `${done} of ${count} steps are marked as done. Open the other tabs to finish them.`);
      rootRef.current?.querySelector(`[id="${id}-more"], [id="${id}-help"]`)?.scrollIntoView({
        block: 'start'
      });
    }
  };
  const onVerify = (stepIndex, checkIndex, value) => {
    const nextVerified = verified.map((list, i) => i === stepIndex ? list.map((v, j) => j === checkIndex ? value : v) : list);
    const nextCompleted = completed.map((done, i) => i === stepIndex && !value ? false : done);
    setVerified(nextVerified);
    setCompleted(nextCompleted);
    persist(nextCompleted, nextVerified);
  };
  const onReset = () => {
    const nextCompleted = steps.map(() => false);
    const nextVerified = steps.map(step => (step.verify || []).map(() => false));
    setCompleted(nextCompleted);
    setVerified(nextVerified);
    persist(nextCompleted, nextVerified);
    select(0);
    say('Your progress in this browser was reset.');
  };
  const onTabKey = event => {
    const moves = {
      ArrowRight: c => (c + 1) % count,
      ArrowLeft: c => (c - 1 + count) % count,
      Home: () => 0,
      End: () => last
    };
    const move = moves[event.key];
    if (!move) return;
    event.preventDefault();
    focusTab.current = true;
    setCurrent(move);
  };
  const openZoom = shot => {
    setZoom(shot);
    const dialog = zoomRef.current;
    if (dialog && !dialog.open) dialog.showModal();
  };
  const closeOnBackdrop = event => {
    const dialog = event.currentTarget;
    if (event.target !== dialog) return;
    const rect = dialog.getBoundingClientRect();
    if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) dialog.close();
  };
  const reveal = targetId => {
    const node = rootRef.current?.querySelector(`[id="${targetId}"]`);
    const details = node && (node.tagName === 'DETAILS' ? node : node.querySelector('details'));
    if (details) details.open = true;
  };
  const renderShot = (shot, stepIndex) => {
    const unavailable = failed[shot.src];
    const label = shot.vendor ? 'VENDOR SCREENSHOT' : 'KOMBIFY SCREENSHOT';
    return <>
        <span className="screenshot-tag">{icon('camera')} {label}</span>
        <Figure className={`screen-figure${shot.frame === 'phone' ? ' phone' : ''}${unavailable ? ' image-unavailable' : ''}`}>
          <div className="screen-image">
            <Img alt={shot.alt} className={shot.srcDark ? 'only-light has-dark' : undefined} decoding="async" height={shot.height} loading={stepIndex === 0 ? 'eager' : 'lazy'} onError={() => setFailed(old => ({
      ...old,
      [shot.src]: true
    }))} referrerPolicy="no-referrer" src={shot.src} width={shot.width} />
            {shot.srcDark ? <Img alt={shot.alt} className="only-dark" decoding="async" height={shot.height} loading="lazy" onError={() => setFailed(old => ({
      ...old,
      [shot.src]: true
    }))} src={shot.srcDark} width={shot.width} /> : null}
            {(shot.vendor ? [] : shot.annotations || []).map((mark, i) => <span aria-hidden="true" className="annotation" data-n={mark.n ?? undefined} key={i} style={{
      top: `${mark.top}%`,
      left: `${mark.left}%`,
      width: `${mark.width}%`,
      height: `${mark.height}%`
    }} />)}
            <Anchor className="image-fallback" href={shot.vendor ? shot.creditHref || shot.src : shot.src} rel="noopener noreferrer" target="_blank">{icon('external')} {shot.vendor ? 'Open the vendor page' : 'Open the screenshot'}<span>{shot.vendor ? 'Loading it needs an internet connection.' : 'The image did not load.'}</span></Anchor>
            <button aria-label={`Enlarge screenshot: ${shot.alt}`} className="zoom-button" onClick={() => openZoom(shot)} type="button">{icon('zoom')}</button>
          </div>
          <Figcaption>
            {shot.caption}{shot.credit ? <> · {shot.creditHref ? link(shot.creditHref, shot.credit) : shot.credit}</> : null}
            {shot.version || shot.captured ? <span className="meta">{[shot.version, shot.captured ? `captured ${shot.captured}` : null].filter(Boolean).join(' · ')}</span> : null}
          </Figcaption>
        </Figure>
      </>;
  };
  const renderLinkVisual = visual => <>
      <span className="screenshot-tag">{icon('external')} {visual.tag || 'VENDOR GUIDE'}</span>
      <div className="link-visual">
        {visual.title ? <strong>{visual.title}</strong> : null}
        {visual.text ? <p>{rich(visual.text)}</p> : null}
        {(visual.links || []).map((item, i) => <Anchor className="vendor-link" href={item.href} key={i} rel="noopener noreferrer" target="_blank">
            {icon(item.icon || 'book')}<span>{item.label}{item.detail ? <small>{item.detail}</small> : null}</span>{icon('external')}<span className="sr-only"> (opens in a new tab)</span>
          </Anchor>)}
      </div>
    </>;
  return <div className="kombify-client-guide not-prose" id={id} lang="en" ref={rootRef} style={{
    '--kcg-steps': count
  }}>
      <div className="guide-content">
        <div className="page-top">
          <div className="intro-meta">{meta.map((item, i) => <span key={i}>{icon(item.icon)} {item.text}</span>)}</div>
          <div className="page-tools"><button aria-label="Print guide" className="tool-button" onClick={() => window.print()} type="button">{icon('print')}</button></div>
        </div>
        {requirements.length ? <div className="ready-strip">
            <strong>What you need</strong>
            {requirements.map((item, i) => <span key={i}>{icon('check')} {rich(item)}</span>)}
            {requirementsHelp ? <Anchor href={`#${requirementsHelp.target}`} onClick={() => reveal(requirementsHelp.target)}>{requirementsHelp.label} {icon('arrow')}</Anchor> : null}
          </div> : null}

        {apps.length ? <section aria-labelledby={`${id}-apps-title`} className="downloads" id={`${id}-apps`}>
            <div className="section-top">
              <h2 id={`${id}-apps-title`}>{appsTitle}</h2>
              {vendor ? link(vendor.href, vendor.label, 'small-link') : null}
            </div>
            <div className="downloads-grid">
              {apps.map((app, i) => <Anchor className="download-card" href={app.href} key={i} rel="noopener noreferrer" target="_blank">
                  <span className="platform-icon">{icon(app.platform)}</span>
                  <span className="download-text">
                    <strong>{app.label}</strong>
                    <small>{app.detail}</small>
                    <span className="store-name">{app.store} {icon('external')}</span>
                    <span className={`badge ${app.official ? 'official' : 'unofficial'}`}>{app.official ? `Official · ${app.vendor}` : `Third-party · ${app.vendor}`}</span>
                  </span>
                  <span className="sr-only"> (opens in a new tab)</span>
                </Anchor>)}
            </div>
            {appsNote ? <p className="download-hint">{rich(appsNote)}</p> : null}
          </section> : null}

        <section aria-labelledby={`${id}-guide-title`} className="guide-section" id={`${id}-guide`}>
          <div className="guide-heading"><h2 id={`${id}-guide-title`}>{stepsTitle}</h2></div>
          <div className="wizard">
            <div aria-label="Setup steps" className="step-tabs" onKeyDown={onTabKey} role="tablist">
              {steps.map((step, i) => <button aria-controls={`${id}-panel-${i}`} aria-selected={i === current} className={`step-tab${completed[i] ? ' done' : ''}`} id={`${id}-tab-${i}`} key={i} onClick={() => select(i)} role="tab" tabIndex={i === current ? 0 : -1} type="button">
                  <span className="step-dot">{completed[i] ? '✓' : i + 1}</span><span>{step.tab}</span>
                </button>)}
            </div>
            {steps.map((step, i) => <Article aria-labelledby={`${id}-tab-${i}`} className={`guide-panel${i === current ? ' active' : ''}`} id={`${id}-panel-${i}`} key={i} role="tabpanel">
                <div className="panel-copy">
                  <div className="step-kicker">STEP {String(i + 1).padStart(2, '0')} / {String(count).padStart(2, '0')}</div>
                  <h3>{step.title}</h3>
                  {step.intro ? <p className="step-intro">{rich(step.intro)}</p> : null}
                  {step.clicks?.length ? <ol className="click-list">{step.clicks.map((click, j) => <li key={j}><span className="click-num">{j + 1}</span><span>{rich(click)}</span></li>)}</ol> : null}
                  {step.verify?.length ? <div className="verify-list">
                      {step.verify.map((item, j) => <label key={j}><input checked={Boolean(verified[i]?.[j])} id={`${id}-verify-${i}-${j}`} onChange={event => onVerify(i, j, event.target.checked)} type="checkbox" /><span>{rich(item)}</span></label>)}
                    </div> : null}
                  {step.note ? <div className="micro-note">{icon('info')}<span>{rich(step.note)}</span></div> : null}
                  {(step.faq ? [].concat(step.faq) : []).map((faq, j) => <Details key={j}><Summary>{faq.q}</Summary><p>{rich(faq.a)}</p></Details>)}
                  {step.check ? <div className="step-check">{icon('ok')} {rich(step.check)}</div> : null}
                </div>
                <div className="panel-visual">
                  {step.screenshot ? renderShot(step.screenshot, i) : step.visual ? renderLinkVisual(step.visual) : null}
                </div>
              </Article>)}
            <div className="wizard-bottom">
              <div className="progress-note">
                <span>{completed.filter(Boolean).length} of {count} steps done</span>
                <div aria-hidden="true" className="progress-meter">{steps.map((step, i) => <span className={completed[i] ? 'done' : undefined} key={i} />)}</div>
              </div>
              <button className="prev" disabled={current === 0} onClick={() => select(current - 1, true)} type="button">{icon('back')} Back</button>
              <button className="next" onClick={onNext} type="button"><span>{current === last ? 'Finish' : 'Done · next'}</span>{icon('arrow')}</button>
            </div>
          </div>
          {sources ? <p className="provenance-inline">{sources.summary} <Anchor href={`#${id}-sources`} onClick={() => reveal(`${id}-sources-detail`)}>Image sources and date</Anchor></p> : null}
        </section>

        {next.length ? <section aria-labelledby={`${id}-more-title`} className="more-section" id={`${id}-more`}>
            <div className="section-top"><h2 id={`${id}-more-title`}>{nextTitle}</h2></div>
            <div className="more-grid">
              {next.map((item, i) => link(item.href, <>
                  <span className="more-icon">{icon(item.icon || 'arrow')}</span>
                  <span><strong>{item.title}</strong><small>{item.text}</small></span>
                </>, 'more-card', i))}
            </div>
          </section> : null}

        {help.length ? <section aria-labelledby={`${id}-help-title`} className="faq-section" id={`${id}-help`}>
            <h2 id={`${id}-help-title`}>{helpTitle}</h2>
            {help.map((item, i) => <Details className="faq" id={item.id} key={i}><Summary>{item.q}</Summary><div className="answer">{rich(item.a)}</div></Details>)}
          </section> : null}

        <Footer className="footline">
          <div className="attribution">{attribution ? rich(attribution) : null}</div>
          <button className="reset" onClick={onReset} type="button">Reset progress</button>
        </Footer>

        {sources ? <section className="source-section" id={`${id}-sources`}>
            <Details id={`${id}-sources-detail`}>
              <Summary>Image sources, checked links and date</Summary>
              <div className="source-content">
                {(sources.paragraphs || []).map((text, i) => <p key={i}>{rich(text)}</p>)}
                <div className="source-links">{(sources.links || []).map((item, i) => link(item.href, item.label, undefined, i))}</div>
              </div>
            </Details>
            <div className="dim">Guide by kombify · Product names, screenshots of vendor apps and trademarks belong to their respective owners.</div>
          </section> : null}
      </div>

      <Dialog aria-labelledby={`${id}-zoom-title`} className="zoom-dialog" onClick={closeOnBackdrop} ref={zoomRef}>
        <div className="dialog-header">
          <h2 id={`${id}-zoom-title`}>{zoom?.vendor ? 'Vendor screenshot' : 'Screenshot'}</h2>
          <button aria-label="Close image" className="dialog-close" onClick={() => zoomRef.current?.close()} type="button">{icon('close')}</button>
        </div>
        <div className="zoom-content">
          {zoom ? <Img alt={zoom.alt} src={zoom.src} /> : null}
          {zoom?.creditHref ? link(zoom.creditHref, zoom.vendor ? 'Open the original from the vendor' : 'About this screenshot') : null}
        </div>
      </Dialog>
      <div aria-live="polite" className="sr-only" role="status">{toast}</div>
      <div aria-hidden="true" className={`toast${toast ? ' show' : ''}`}>{toast}</div>
    </div>;
};

<ClientSetupGuide
  id="kcg-passwords"
  meta={[
{ icon: "phone", text: "Phone, browser & computer" },
{ icon: "steps", text: "6 short steps" },
{ icon: "book", text: "No prior knowledge needed" },
]}
  requirements={[
"Your vault address, for example https://vault.home.test",
"An invitation for your email address",
"Each device set up for your home vault by the person who set up kombify",
"A master password you can remember",
]}
  requirementsHelp={{ label: "No invitation yet?", target: "kcg-passwords-no-invite" }}
  appsTitle="First, get the Bitwarden app."
  vendor={{ label: "All downloads from Bitwarden", href: "https://bitwarden.com/download/" }}
  apps={[
{ platform: "apple", label: "iPhone & iPad", detail: "Bitwarden Password Manager", store: "Open in the App Store", href: "https://apps.apple.com/app/bitwarden-password-manager/id1137397744", vendor: "Bitwarden", official: true },
{ platform: "android", label: "Android", detail: "Bitwarden Password Manager", store: "Open in Google Play", href: "https://play.google.com/store/apps/details?id=com.x8bit.bitwarden", vendor: "Bitwarden", official: true },
{ platform: "browser", label: "Browser extension", detail: "Chrome, Firefox, Edge, Safari and more", store: "Open the Bitwarden download page", href: "https://bitwarden.com/download/", vendor: "Bitwarden", official: true },
{ platform: "desktop", label: "Computer", detail: "Windows, macOS and Linux", store: "Open the Bitwarden download page", href: "https://bitwarden.com/download/", vendor: "Bitwarden", official: true },
]}
  appsNote="Browser stores: [Chrome](https://chromewebstore.google.com/detail/bitwarden-password-manager/nngceckbapebfimnlniiiahkandclblb) · [Firefox](https://addons.mozilla.org/firefox/addon/bitwarden-password-manager/) · [Edge](https://microsoftedge.microsoft.com/addons/detail/bitwarden-password-manager/jbkfoedolllekgbhcbcoahefnbanhhlh) · [Safari (Mac App Store)](https://apps.apple.com/app/bitwarden/id1352778147). Your vault runs on Vaultwarden, which works with these official Bitwarden apps. You do not need a separate Vaultwarden app."
  stepsTitle="Set up your vault step by step."
  steps={[
{
  tab: "Open vault",
  title: "Open your vault in a browser.",
  intro: "Start on a computer. Your vault has its own web address, like a website that only your household uses.",
  clicks: [
    "Type your vault address into the browser's address bar, for example **https://vault.home.test**, and press Enter.",
    "Below the login box, click [[Create account]].",
  ],
  note: "The address in this guide is an example. Use the address you got from the person who set up kombify.",
  faq: { q: "What is a vault address?", a: "It is the web address of your household's password vault. It usually starts with **https://vault.** and ends with your home network's name. The person who set up kombify can tell you the exact address." },
  check: "You see the [[Create account]] form.",
  screenshot: { src: "/images/guides/passwords/vaultwarden/web-01-open-vault.webp", width: 1088, height: 830, alt: "Vaultwarden login page with the Create account link marked", caption: "Vault login page", credit: "kombify screenshot", creditHref: "#kcg-passwords-sources", version: "Vaultwarden 1.37.2 · web vault 2026.7.0", captured: "2026-09-24", annotations: [{ n: 2, top: 84.3, left: 38.3, width: 23.5, height: 7.2 }] },
},
{
  tab: "Create account",
  title: "Create your account.",
  intro: "Your vault only accepts email addresses that were invited. Use the same address the invitation went to.",
  clicks: [
    "In [[Email address]], enter your invited email address.",
    "In [[Name]], enter your name. The person who runs the vault, and anyone you share passwords with, can see it.",
    "Click [[Continue]].",
  ],
  faq: { q: "Is this my kombify account?", a: "No. This account belongs only to your password vault. It has its own email address and its own master password." },
  check: "The page now asks you to [[Set a strong password]].",
  screenshot: { src: "/images/guides/passwords/vaultwarden/web-02-create-account.webp", width: 1088, height: 718, alt: "Create account form with the email address alex@example.com and the name Alex Example", caption: "Create account", credit: "kombify screenshot", creditHref: "#kcg-passwords-sources", version: "Vaultwarden 1.37.2 · web vault 2026.7.0", captured: "2026-09-24", annotations: [{ n: 1, top: 37.6, left: 16.4, width: 65.1, height: 13.4 }, { n: 2, top: 61, left: 16.4, width: 65.1, height: 13.4 }, { n: 3, top: 76.3, left: 14, width: 72.1, height: 13.9 }] },
},
{
  tab: "Master password",
  title: "Choose your master password.",
  intro: "The master password is the one password you still need to remember. It unlocks all the others.",
  clicks: [
    "In [[Master password]], enter a long password. A short sentence of four or more words works well.",
    "Enter the same password again in [[Confirm master password]].",
    "Click [[Create account]]. If the page suggests the browser extension, click [[Add it later]] and then [[Skip to web app]]. You connect the extension in step 5.",
  ],
  note: "**Nobody can recover a forgotten master password**, not even the person who runs your vault. Write it down and keep the note somewhere safe outside the vault.",
  faq: { q: "What does the strength bar mean?", a: "It estimates how hard your password is to guess. Aim for **Strong**. Longer is better than complicated." },
  check: "Your empty vault opens with the title [[All vaults]].",
  screenshot: { src: "/images/guides/passwords/vaultwarden/web-03-master-password.webp", width: 1088, height: 1310, alt: "Set a strong password page with the master password entered twice and rated Strong", caption: "Set a strong password", credit: "kombify screenshot", creditHref: "#kcg-passwords-sources", version: "Vaultwarden 1.37.2 · web vault 2026.7.0", captured: "2026-09-24", annotations: [{ n: 1, top: 25.5, left: 16.4, width: 60.7, height: 7.3 }, { n: 2, top: 52.2, left: 16.4, width: 60.7, height: 7.3 }, { n: 3, top: 87, left: 14, width: 72.1, height: 7.6 }] },
},
{
  tab: "First password",
  title: "Save your first password.",
  intro: "Save one login as a test. It shows later that your apps are connected to your vault.",
  clicks: [
    "At the top right, click [[New]] and choose [[Login]]. If a welcome window appears first, click [[Skip]].",
    "In [[Item name]], enter a name you recognize, for example the name of a shop.",
    "Enter your [[Username]]. Next to [[Password]], click the circular arrows to create a password, then click [[Use this password]].",
    "Click [[Save]].",
  ],
  note: "Add the shop's web address under [[Website (URI)]]. The browser extension then suggests this login on that website.",
  check: "The message [[Item added]] appears and your login shows in the list.",
  screenshot: { src: "/images/guides/passwords/vaultwarden/web-04-new-login.webp", width: 1472, height: 1664, alt: "New Login window with the item name Example Shop, a username and a generated password", caption: "New Login", credit: "kombify screenshot", creditHref: "#kcg-passwords-sources", version: "Vaultwarden 1.37.2 · web vault 2026.7.0", captured: "2026-09-24", annotations: [{ n: 2, top: 12.1, left: 7.9, width: 82.6, height: 5.8 }, { n: 3, top: 41.8, left: 7.9, width: 79.3, height: 5.8 }, { n: 3, top: 52.3, left: 87.5, width: 5.7, height: 5 }, { n: 4, top: 92.1, left: 2.7, width: 10.4, height: 6 }] },
},
{
  tab: "Connect apps",
  title: "Connect the Bitwarden apps.",
  intro: "Each Bitwarden app first connects to Bitwarden's own servers. Tell it once to use your vault instead.",
  clicks: [
    "Install the Bitwarden app from the list above and open it.",
    "On the login screen, open the [[Logging in on]] menu and choose [[Self-hosted]]. In the computer app, the menu is called [[Accessing]].",
    "In [[Server URL]], enter your vault address with **https://** at the start, then tap [[Save]].",
    "Log in with your email address and master password.",
  ],
  note: "Repeat these steps on every phone, browser and computer you use. Each one needs your vault address once.",
  faq: { q: "Why is there no picture of the app here?", a: "Bitwarden's terms give no permission to reuse its pictures. Its own guide shows each screen, and the labels above match it." },
  check: "The app shows the login you saved in step 4.",
  visual: {
    tag: "BITWARDEN GUIDE",
    title: "Bitwarden shows each screen",
    text: "Pick your device in Bitwarden's guide. Look for the section about a **self-hosted** server.",
    links: [
      { label: "Connect an app to your own server", detail: "bitwarden.com · all devices", href: "https://bitwarden.com/help/change-client-environment/", icon: "server" },
      { label: "Get started with the browser extension", detail: "bitwarden.com", href: "https://bitwarden.com/help/getting-started-browserext/", icon: "browser" },
      { label: "Get started with the mobile app", detail: "bitwarden.com", href: "https://bitwarden.com/help/getting-started-mobile/", icon: "phone" },
    ],
  },
},
{
  tab: "Check",
  title: "Is everything connected?",
  intro: "Check it in the web vault and in one app. Only tick what you checked yourself.",
  verify: [
    "I can open my vault address and log in.",
    "The login I saved shows up in a Bitwarden app on my phone, browser or computer.",
    "My master password is written down and kept outside the vault.",
  ],
  note: "This guide does not read your vault. Your ticks stay in this browser.",
  check: "Now you can move your other passwords into the vault.",
  screenshot: { src: "/images/guides/passwords/vaultwarden/web-05-first-item.webp", width: 1430, height: 300, alt: "All vaults list showing the saved Example Shop login", caption: "Your first login in the vault", credit: "kombify screenshot", creditHref: "#kcg-passwords-sources", version: "Vaultwarden 1.37.2 · web vault 2026.7.0", captured: "2026-09-24", annotations: [{ n: null, top: 37.2, left: 1, width: 98, height: 58.1 }] },
},
]}
  nextTitle="What comes next."
  next={[
{ icon: "shield", title: "Turn on two-step login", text: "Bitwarden explains how to add a second login step.", href: "https://bitwarden.com/help/setup-two-step-login/" },
{ icon: "server", title: "Back up the vault", text: "For the person who runs kombify: protect the vault data.", href: "/guides/stackkits/back-up-and-restore" },
]}
  help={[
{ id: "kcg-passwords-no-invite", q: "I don't have a vault address or an invitation yet.", a: "Ask the person who set up kombify in your household. They add the Passwords service and invite your email address. [How Passwords works with kombify](/guides/stackkits/use-cases/passwords) · [Owner setup](/stackkits/apps/vaultwarden)." },
{ q: "Create account says “Registration not allowed or user already exists”.", a: "Check that you use exactly the invited email address. If you already created your account, go back and log in instead. Otherwise, ask for a new invitation." },
{ q: "The address does not open, or the browser warns about the connection.", a: "Try the address on a device in your home network first. If your vault runs only at home, connect to your home Wi-Fi or to the home connection app you were given. Each device must also be set up to trust your home server; the person who set up kombify does this. If the browser says the connection is not private, stop there and ask them. Do not click past the warning. [About home addresses](/guides/stackkits/use-cases/network)." },
{ q: "I forgot my master password.", a: "Nobody can recover it. Your vault is encrypted with it. The person who runs your vault can delete your account so you can start again, but the passwords saved in it are lost. [Bitwarden on master passwords](https://bitwarden.com/help/master-password/)." },
{ q: "My Android phone has no Google Play.", a: "Bitwarden runs its own F-Droid repository. The address is listed on the [Bitwarden download page](https://bitwarden.com/download/)." },
{ q: "Why does it say Vaultwarden and Bitwarden?", a: "Vaultwarden is the program that runs your vault. It is an unofficial server that works with the official Bitwarden apps. Vaultwarden is not associated with Bitwarden, Inc. The pictures show Vaultwarden 1.37.2 in English; newer versions and other languages can look different." },
]}
  attribution="**Vaultwarden runs your vault, and the Bitwarden apps open it. kombify installs it for you.** kombify makes neither app. [Vaultwarden](https://github.com/dani-garcia/vaultwarden) (AGPL-3.0) · web vault: [Bitwarden web client built for Vaultwarden](https://github.com/dani-garcia/bw_web_builds) (GPL-3.0) · [Bitwarden](https://bitwarden.com/)"
  sources={{
summary: "Screenshots by kombify from a local Vaultwarden install · made-up account · English menus.",
paragraphs: [
  "**Screenshots · September 24, 2026.** kombify captured the web vault pictures from a local reference install: Vaultwarden 1.37.2, the image the StackKits catalog delivers for Passwords, with its bundled web vault 2026.7.0 (the Bitwarden web client, GPL-3.0). The install used the example address https://vault.home.test and the made-up account alex@example.com. It is a reference install, not a StackKits rollout test. This guide added the violet markers.",
  "The Bitwarden app screens are not shown. [Bitwarden's terms](https://bitwarden.com/terms/) give no permission to reuse its content, so step 5 links to Bitwarden's own guide.",
  "Store links and help pages were checked on September 24, 2026. Your progress stays in this browser. The guide connects no accounts and asks for no passwords.",
],
links: [
  { label: "Bitwarden: connect individual apps", href: "https://bitwarden.com/help/change-client-environment/" },
  { label: "Bitwarden downloads", href: "https://bitwarden.com/download/" },
  { label: "Vaultwarden project", href: "https://github.com/dani-garcia/vaultwarden" },
],
}}
/>
