Feature Spec · Draft In Planning

Autofill Plan

Key Vault → Web Forms

Auto-detect login forms, match against vault entries by domain, and fill credentials on user click — without ever exposing secrets to the page.

Overview

When a user visits a login page, the extension detects <input> fields for username/email and password, matches the page URL against Key Vault entries that have a url field, and offers to auto-fill the credentials.

The fill action is always user-initiated — a floating badge appears near detected password fields and credentials are only inserted after an explicit click. Secrets are never logged, never stored in content-script memory beyond the fill operation, and the vault must be unlocked before any credential is surfaced.

Architecture

Content Script (login pages) ↕ chrome.runtime.sendMessage Service Worker ↕ chrome.runtime.sendMessage Offscreen Document (decrypt secret value) ↕ response back through chain Content Script fills the form

Steps

01

Step 1

Content script for login detection

Create src/content/autofill.ts — runs on all HTTP(S) pages.

  • On page load, scan for <input type="password"> and nearby text/email inputs
  • If a password field is found, the page is a login form candidate
  • Extract the page's domain (e.g. github.com from https://github.com/login)
  • Send { type: 'AUTOFILL_LOOKUP', payload: { domain } } to service worker
  • If matches found, inject a small floating "My SPACE" badge near the password field
  • User clicks the badge → extension sends AUTOFILL_GET to decrypt the value → fills the form

Form detection heuristics

1. Find all <input type="password"> elements
2. For each password field, walk up to the nearest <form> or container
3. Within that container, find:
   - <input type="email"> or <input type="text"> nearest above
     the password field → username
   - The password field itself → password
4. If no <form>, look for inputs within a shared parent div

URL matching logic

  • Store the url field on secrets as a full URL (e.g. https://github.com/login)
  • Match by domain: extract hostname from the secret's url and compare with the current page's hostname
  • If multiple secrets match the same domain, show all matches in a dropdown
  • If the secret's url path matches the current path, prioritise it over domain-only matches
02

Step 2

New message types

Add to src/shared/messages.ts:

typescript// Content script → service worker: "are there secrets for this domain?"
export type AutofillLookupMsg = Msg<'AUTOFILL_LOOKUP', { domain: string }>

// Service worker → offscreen: "decrypt this secret's value"
export type AutofillGetMsg = Msg<'AUTOFILL_GET', { id: string }>
03

Step 3

Service worker handler

In src/service-worker/index.ts:

  • AUTOFILL_LOOKUP — Query SECRETS_LIST from offscreen, filter by url containing the domain, return matching SecretMeta[] (label + id only, no values)
  • AUTOFILL_GET — Forward to offscreen as SECRETS_GET, return decrypted value
  • Both require vault to be unlocked — if locked, return { ok: false, error: 'locked' } and the content script shows a "Unlock My SPACE" prompt
04

Step 4

Content script UI

Inject a small floating badge near detected password fields:

┌─────────────────────────┐
│  🔑 My SPACE            │
│  GitHub Token           │
│  Click to auto-fill     │
└─────────────────────────┘
  • Positioned above or beside the password field
  • Shows matching secret labels
  • On click: sends AUTOFILL_GET, fills username + password fields
  • If vault locked: badge says "Unlock My SPACE" and opens side panel on click
  • Auto-dismisses after 30 seconds or if the form is submitted
05

Step 5

Manifest changes

Add to manifest.json content_scripts. Map pages are excluded — they already have the pin button content script.

json{
  "matches": ["<all_urls>"],
  "js": ["src/content/autofill.ts"],
  "run_at": "document_idle",
  "exclude_matches": [
    "https://www.google.com/maps/*",
    "https://www.openstreetmap.org/*",
    "https://www.bing.com/maps/*",
    "https://maps.apple.com/*"
  ]
}
06

Step 6

Security considerations

Never log decrypted values — no console output, no analytics events containing credential data

Clear immediately after fill — decrypted values are removed from memory as soon as form fields are populated

User-consent only — credentials are never auto-filled without an explicit user click on the badge

Badge auto-hides after 30s — the UI is transient and does not persist on the page indefinitely

Minimal memory footprint — no values stored in content-script memory longer than strictly needed for the fill operation

Vault must be unlocked — if vault is locked, the badge prompts the user to open the side panel, no credentials are surfaced

Implementation Order

1

Add message types (AUTOFILL_LOOKUP, AUTOFILL_GET) to messages.ts

2

Add handlers in service-worker/index.ts

3

Add offscreen routing for AUTOFILL_GET (reuse SECRETS_GET)

4

Create src/content/autofill.ts with form detection + badge UI

5

Update manifest.json with content script for all URLs

6

Test on common login page patterns (GitHub, Gmail, etc.)

7

Add tests for URL domain matching logic