diff --git a/tests/wizard.php b/tests/wizard.php
index 6d86cd6..9222ba3 100644
--- a/tests/wizard.php
+++ b/tests/wizard.php
@@ -75,4 +75,43 @@
'');
is_same ('an empty state emits nothing', state_block([]), '');
+group('Confirm email / password (typo protection)');
+$ok = ['org'=>'A','email'=>'me@example.com','email2'=>'me@example.com','username'=>'',
+ 'password'=>'correct-horse','password2'=>'correct-horse','agent'=>''];
+is_same ('a matching pair passes', admin_errors($ok), '');
+is_same ('missing email is caught', admin_errors(['email'=>'']+$ok), 'Enter an admin email address.');
+is_true ('mismatched email is caught', admin_errors(['email2'=>'you@example.com']+$ok) !== '');
+// cPanel users retype with different capitalisation constantly; that is not a typo.
+is_same ('email match is case-insensitive', admin_errors(['email2'=>'ME@Example.com']+$ok), '');
+is_same ('missing password is caught', admin_errors(['password'=>'']+$ok), 'Choose a password.');
+is_true ('mismatched password is caught', admin_errors(['password2'=>'correct-horse ']+$ok) !== '');
+is_true ('password match is EXACT', admin_errors(['password2'=>'Correct-Horse']+$ok) !== '');
+
+group('The admin form asks for both confirmations');
+$af = admin_form($ok);
+is_true ('confirm email field', (bool) preg_match('/name="email2"/', $af));
+is_true ('confirm password field', (bool) preg_match('/name="password2"/', $af));
+is_false('neither rides in the hidden bag',
+ (bool) preg_match('/type="hidden" name="(email2|password2)"/', $af));
+
+group('The credentials backup file');
+$bag = $ok + ['db_host'=>'localhost','db_name'=>'cp_tiger','db_user'=>'cp_tiger','db_pass'=>'dbsecret',
+ 'app_dir'=>'/home/u/site/tiger-app','docroot'=>'/home/u/public_html'];
+$plain = credentials_file($bag, 'https://example.com', ['ok'=>false]);
+foreach ([['the admin email','me@example.com'], ['the admin password','correct-horse'],
+ ['the db name','cp_tiger'], ['the db password','dbsecret'],
+ ['the app dir','/home/u/site/tiger-app'], ['the config path','local.ini'],
+ ['a keep-it-safe warning','CONTAINS PASSWORDS']] as $c) {
+ is_true('carries ' . $c[0], strpos($plain, $c[1]) !== false);
+}
+is_false('no agent section when it was not enabled', strpos($plain, 'AGENT ACCESS') !== false);
+
+$withAgent = credentials_file($bag, 'https://example.com',
+ ['ok'=>true, 'token'=>'tgr_abc', 'modules'=>['cms','media']]);
+is_true ('agent section when enabled', strpos($withAgent, 'AGENT ACCESS') !== false);
+is_true ('carries the access key', strpos($withAgent, 'tgr_abc') !== false);
+is_true ('says it cannot be retrieved later', stripos($withAgent, 'shown once') !== false);
+is_true ('says where to revoke', strpos($withAgent, '/mcp/admin') !== false);
+
+
done();
diff --git a/tiger-install.php b/tiger-install.php
index 0290cc6..448ef57 100644
--- a/tiger-install.php
+++ b/tiger-install.php
@@ -29,7 +29,7 @@
@ini_set('display_errors', '1');
@set_time_limit(0);
-const INSTALLER_VERSION = '1.1.0';
+const INSTALLER_VERSION = '1.2.0';
const RELEASE_REPO = 'webtigers/tiger'; // the skeleton repo whose releases host the full-app bundle
const MIN_PHP = '8.1.0';
const GH_API = 'https://api.github.com';
@@ -410,12 +410,14 @@ function db_form($bag, $errNote = '') {
function admin_form($bag, $errNote = '') {
return '