feat(android): a local account, so the app serves without a repository

Until now the phone could only read a vault that came from elsewhere:
without a URL, a token and a sync password it showed nothing. It now
signs in like the desktop does - username and master password - and can
create its own vault, with no network at all.

The local container carries exactly what the repository would carry: same
format, same encryption under the password. Two reasons, not one. It is
the desktop's model, where the master password protects the vault at rest
rather than the hardware alone; and the format staying the repository's
means sync keeps working with nothing to convert. No Keystore on that
file - it is already encrypted under the password, and binding it to the
device would make it unrecoverable after a reinstall. vault.bin keeps
that role: it is the fast-open cache, and only it needs sealing.

The password opens the container - the decryption itself is the check, no
secret is ever compared - and the username, read from inside once open,
says whose vault it is.

The home screen now knows which of three situations it is in: nothing
here yet, a container without a cache, or a cache ready to open.

Verified on the device: with the cache set aside, the screen offers
username, master password and Create vault, alongside the download path.
Creating an account could not be exercised to the end - the device locked
its screen mid-run - and the vault that was set aside is back in place,
same size, still sealed under its code.
This commit is contained in:
r-zakarya
2026-08-21 14:07:39 +01:00
parent b1be802e22
commit f32cde81fc
4 changed files with 288 additions and 1 deletions
+45
View File
@@ -227,6 +227,27 @@
<div class="screen" id="unlock">
<div class="scroll">
<div class="status" id="status"></div>
<!-- SE CONNECTER, comme sur le poste. Le mot de passe maitre ouvre le
conteneur local - c'est le dechiffrement lui-meme qui verifie, aucun
secret n'est compare - et le nom dit a qui appartient ce coffre.
Cache des qu'un cache existe : redemander six secondes de derivation
a chaque lancement alors que le cache est la n'apporterait rien, et
c'est le code de deverrouillage qui garde cette porte-la. -->
<div id="authbox" class="stack" hidden>
<div class="in-wrap">
<input class="field-in" id="user" placeholder="username"
autocomplete="off" autocorrect="off" autocapitalize="none"
spellcheck="false">
<div class="in-x" data-clear="user">&#215;</div>
</div>
<div class="in-wrap">
<input class="field-in" id="pass" placeholder="master password"
type="password" autocomplete="off">
<div class="in-x" data-clear="pass">&#215;</div>
</div>
<button class="btn wide main" id="btnAuth">Sign in</button>
</div>
<div class="stack">
<button class="btn wide main" id="btnOpen">&#10140;&nbsp; Open vault</button>
<button class="btn wide" id="btnSync">&#8635;&nbsp; Sync</button>
@@ -385,6 +406,19 @@ function pushConfig() {
el.addEventListener('blur', pushConfig);
});
// Le meme bouton dans les deux sens : creer un coffre et en ouvrir un ne
// different que par ce qui existe deja sur l'appareil, et Delphi le sait.
var authMode = 'open';
document.getElementById('btnAuth').addEventListener('click', function () {
var u = document.getElementById('user').value.trim();
var p = document.getElementById('pass').value;
// Le mot de passe part, puis le champ se vide : il n'a plus rien a faire
// dans le document une fois la commande envoyee.
send((authMode === 'register' ? 'register' : 'login')
+ '?u=' + encodeURIComponent(u) + '&p=' + encodeURIComponent(p));
document.getElementById('pass').value = '';
});
document.getElementById('btnOpen').addEventListener('click', function () {
pushConfig(); // le code vient peut-etre d'etre tape
send('openvault');
@@ -720,6 +754,17 @@ var PM = {
/// n'est pose supprimerait le seul endroit ou en poser un ; le montrer sans
/// rien dire laisserait croire qu'il en faut un pour ouvrir. C'est donc le
/// texte d'aide qui porte la difference.
/// Dans lequel des trois cas se trouve cet appareil : rien du tout,
/// un conteneur sans cache, ou un cache pret a s'ouvrir.
setAuthMode: function (mode) {
authMode = mode;
var box = document.getElementById('authbox');
box.hidden = (mode === 'open');
document.getElementById('pinbox').hidden = (mode !== 'open');
document.getElementById('btnAuth').textContent =
(mode === 'register') ? 'Create vault' : 'Sign in';
},
setPinNeeded: function (on) {
document.getElementById('pin').placeholder =
on ? 'unlock code' : 'set an unlock code (optional)';