diff --git a/.gitignore b/.gitignore
index 63d4101..cec3549 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,3 +56,7 @@ delphi-backend/vault - Copie*.db
*.png
*.jpg
*.jpeg
+
+# Delphi build artifacts
+delphi-backend/*.vrc
+delphi-backend/*.$manifest
diff --git a/CLAUDE.md b/CLAUDE.md
index 48fab6b..20ec08d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -33,6 +33,7 @@ visibles des suivants exactement comme dans le monofichier. Ordre actuel :
```
js/argon2.js (IIFE, globalThis.NobleArgon2)
js/app.crypto.js (KDF, verifier, encrypt/decrypt — extrait §3.1)
+js/app.totp.js (TOTP RFC 6238 + TOTP/custom-field crypto — extrait §3.1)
js/app.import.js (export container + import CSV/JSON — extrait §3.1)
js/app.js (le reste : state, Bridge, api, UI…)
js/app.sync.js (WebDAV + merge — extrait §3.1, APRÈS app.js car
@@ -75,9 +76,10 @@ invariant crypto/merge cassé avorte le build comme une erreur de syntaxe.
via `npm test`. Voir [js/tests/README.md](js/tests/README.md) — le harness
concatène `APP_PARTS` (`app.crypto.js` + `app.js`) et les charge dans un
`node:vm` avec les globals navigateur stubbés (`argon2.js` chargé à part,
-c'est un IIFE), puis expose les internals via un épilogue d'export. Couvre : round-trip crypto + dérivation verifier (legacy vs -v2),
-parsing CSV d'import, et l'arbitrage merge/tombstone de sync
-(`applyRemoteSnapshot`, seule `api()` est stubbée).
+c'est un IIFE), puis expose les internals via un épilogue d'export. Couvre : round-trip crypto + dérivation verifier (legacy vs -v2)
++ Argon2id (vecteur RFC 9106), TOTP (vecteurs RFC 6238), parsing CSV
+d'import, et l'arbitrage merge/tombstone de sync (`applyRemoteSnapshot`,
+seule `api()` est stubbée).
## Carte des fichiers
@@ -99,6 +101,7 @@ parsing CSV d'import, et l'arbitrage merge/tombstone de sync
| Handlers REST | `delphi-backend/Handlers/PM.Handler.*.pas` |
| Frontend principal (en cours de découpage §3.1) | `js/app.js` |
| Crypto frontend (KDF, verifier, AES-GCM) — extrait §3.1 | `js/app.crypto.js` |
+| TOTP (RFC 6238) + TOTP/custom-field crypto — extrait §3.1 | `js/app.totp.js` |
| Import/export frontend (CSV/JSON parse, export container) — extrait §3.1 | `js/app.import.js` |
| Sync frontend (WebDAV, snapshot, merge) — extrait §3.1 | `js/app.sync.js` |
| Argon2id vendé (bundle `@noble/hashes`, IIFE) | `js/argon2.js` |
diff --git a/CODE_AUDIT.md b/CODE_AUDIT.md
index 77242ed..f003f40 100644
--- a/CODE_AUDIT.md
+++ b/CODE_AUDIT.md
@@ -233,9 +233,13 @@ réécriture des call-sites, risque quasi nul vs conversion en modules ES).
`doImport`/`doExport`) — byte-for-byte identique, couvert par les 14 tests
CSV, chargé AVANT app.js (pures déclarations). Pattern + règles d'ordre
documentés dans CLAUDE.md « Découpage frontend ».
-- `app.js` : 11 936 → **10 253 lignes** (crypto + sync + import sortis).
+- ✅ `js/app.totp.js` extrait (TOTP RFC 6238 + crypto TOTP/custom-fields) —
+ byte-for-byte identique, chargé AVANT app.js. **+13 tests TOTP (vecteurs
+ RFC 6238)** ajoutés en même temps → extraction *et* nouvelle couverture.
+- `app.js` : 11 936 → **10 138 lignes** (crypto + totp + sync + import sortis).
+- Suite de tests : 42 → **55 tests**.
- Reste à extraire (grosses sections cohésives) : slideover, settings,
- quicksearch, autofill, auto-backup…
+ quicksearch, autofill, auto-backup, favicons…
- ✅ `node --check` en pré-étape de `BuildAssets.ps1` : **déjà fait** (cf. §3.2).
### 3.2 🟡 Aucun test automatisé — **partiellement adressé (2026-07-04)**
diff --git a/delphi-backend/assets/BuildAssets.ps1 b/delphi-backend/assets/BuildAssets.ps1
index d947c5e..a1745d3 100644
--- a/delphi-backend/assets/BuildAssets.ps1
+++ b/delphi-backend/assets/BuildAssets.ps1
@@ -53,6 +53,7 @@ $patterns = @(
'index.html',
'js\argon2.js',
'js\app.crypto.js',
+ 'js\app.totp.js',
'js\app.import.js',
'js\app.js',
'js\app.sync.js',
diff --git a/delphi-backend/assets/assets.inc b/delphi-backend/assets/assets.inc
index 9875e95..f3742f6 100644
--- a/delphi-backend/assets/assets.inc
+++ b/delphi-backend/assets/assets.inc
@@ -1,10 +1,12 @@
// Auto-generated by BuildAssets.ps1 - do not edit by hand.
const
- EMBEDDED_ASSET_COUNT = 6;
+ EMBEDDED_ASSET_COUNT = 8;
EMBEDDED_ASSETS: array[0..EMBEDDED_ASSET_COUNT-1] of TEmbeddedAsset = (
(UrlPath: '/index.html'; ResName: 'INDEX_HTML'),
(UrlPath: '/js/argon2.js'; ResName: 'JS_ARGON2_JS'),
(UrlPath: '/js/app.crypto.js'; ResName: 'JS_APP_CRYPTO_JS'),
+ (UrlPath: '/js/app.totp.js'; ResName: 'JS_APP_TOTP_JS'),
+ (UrlPath: '/js/app.import.js'; ResName: 'JS_APP_IMPORT_JS'),
(UrlPath: '/js/app.js'; ResName: 'JS_APP_JS'),
(UrlPath: '/js/app.sync.js'; ResName: 'JS_APP_SYNC_JS'),
(UrlPath: '/css/style.css'; ResName: 'CSS_STYLE_CSS')
diff --git a/delphi-backend/assets/assets.rc b/delphi-backend/assets/assets.rc
index be440e3..a00d2ff 100644
--- a/delphi-backend/assets/assets.rc
+++ b/delphi-backend/assets/assets.rc
@@ -4,6 +4,8 @@
INDEX_HTML RCDATA "Z:\\password-manager\\index.html"
JS_ARGON2_JS RCDATA "Z:\\password-manager\\js\\argon2.js"
JS_APP_CRYPTO_JS RCDATA "Z:\\password-manager\\js\\app.crypto.js"
+JS_APP_TOTP_JS RCDATA "Z:\\password-manager\\js\\app.totp.js"
+JS_APP_IMPORT_JS RCDATA "Z:\\password-manager\\js\\app.import.js"
JS_APP_JS RCDATA "Z:\\password-manager\\js\\app.js"
JS_APP_SYNC_JS RCDATA "Z:\\password-manager\\js\\app.sync.js"
CSS_STYLE_CSS RCDATA "Z:\\password-manager\\css\\style.css"
diff --git a/delphi-backend/assets/assets.res b/delphi-backend/assets/assets.res
index a04b0bc..81c3200 100644
Binary files a/delphi-backend/assets/assets.res and b/delphi-backend/assets/assets.res differ
diff --git a/index.html b/index.html
index 71ac05d..38bccba 100644
--- a/index.html
+++ b/index.html
@@ -1194,6 +1194,7 @@
+
diff --git a/js/app.js b/js/app.js
index 90be4c1..be1d94a 100644
--- a/js/app.js
+++ b/js/app.js
@@ -713,124 +713,9 @@ async function api(path, opts) {
}
// ============================================================
-// TOTP (RFC 6238) — 6-digit time-based codes
+// TOTP + TOTP/custom-field crypto — extracted to js/app.totp.js
+// (§3.1), loaded as a separate