fix(auth)!: the recovery phrase is proved by a key of its own
A key proof was a signature by the account's signing key, which every signed-in device keeps in its keyring: whoever could read one, with a code from its authenticator, set a new password without the password or the phrase (the second reading's H1). The phrase now proves itself with an Ed25519 key derived from its bits alone (halkyon_keys' recoveryProofKeysOf, KDF context hlkrprf1, a sibling of the recovery key), whose public half every setup, new phrase and account reset sends and the register keeps (account_password.recoveryProofKey); the device derives the secret half only while the phrase is typed. An account without one (none exist outside development) proves no phrase until it sets a new one; decoys check against a decoy proof key alike. Migration (module, Learn's and Todo's servers, Learn's device schema): ALTER TABLE "account_password" ADD COLUMN "recoveryProofKey" bytea; nullable, so rows made before keep working and no data is touched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
38bb090575
commit
aea0c447fc
28 changed files with 40952 additions and 93 deletions
|
|
@ -209,8 +209,10 @@ Built on the three parts above, on the register (members forward every step thro
|
|||
- **Resets:** a lost authenticator: SRP's proof plus a mailed code or the recovery
|
||||
phrase's signature asks for it; it takes effect 72 hours later, a mail tells the
|
||||
address at once, and signing in with a code calls it off; then the password alone
|
||||
signs in until a new authenticator is confirmed. A lost password with the phrase: the
|
||||
account's Ed25519 signature over a nonce (`keyProofMessage`) and a code set a new
|
||||
signs in until a new authenticator is confirmed. A lost password with the phrase: a
|
||||
signature over a nonce (`keyProofMessage`) by the phrase's own proof key (Ed25519,
|
||||
derived from the phrase alone, its public half kept as `recoveryProofKey`; never the
|
||||
account's signing key, which every signed-in device keeps) and a code set a new
|
||||
password, the keys staying. A lost password without it: a mailed code asks, 72 hours
|
||||
pass (a mail at the start, a sign-in calls it off), a new code carries it out: new
|
||||
password, new keys, no authenticators.
|
||||
|
|
|
|||
|
|
@ -759,6 +759,11 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
type: _is.getType<_iji0wmu8.WrappedKeys>(),
|
||||
nullable: false,
|
||||
),
|
||||
'recoveryProofKey': _is.ParameterDescription(
|
||||
name: 'recoveryProofKey',
|
||||
type: _is.getType<_idt.ByteData>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
|
|
@ -773,6 +778,7 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
totpCode: params['totpCode'],
|
||||
recoveryCode: params['recoveryCode'],
|
||||
recoveryKeys: params['recoveryKeys'],
|
||||
recoveryProofKey: params['recoveryProofKey'],
|
||||
),
|
||||
),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ abstract class AccountPassword
|
|||
required this.passwordKeysCipherText,
|
||||
required this.recoveryKeysNonce,
|
||||
required this.recoveryKeysCipherText,
|
||||
this.recoveryProofKey,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
});
|
||||
|
|
@ -53,6 +54,7 @@ abstract class AccountPassword
|
|||
required _idt.ByteData passwordKeysCipherText,
|
||||
required _idt.ByteData recoveryKeysNonce,
|
||||
required _idt.ByteData recoveryKeysCipherText,
|
||||
_idt.ByteData? recoveryProofKey,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
}) = _AccountPasswordImpl;
|
||||
|
|
@ -88,6 +90,11 @@ abstract class AccountPassword
|
|||
recoveryKeysCipherText: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['recoveryKeysCipherText'],
|
||||
),
|
||||
recoveryProofKey: jsonSerialization['recoveryProofKey'] == null
|
||||
? null
|
||||
: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['recoveryProofKey'],
|
||||
),
|
||||
createdAt: _is.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['createdAt'],
|
||||
),
|
||||
|
|
@ -140,6 +147,13 @@ abstract class AccountPassword
|
|||
/// …and what it sealed.
|
||||
_idt.ByteData recoveryKeysCipherText;
|
||||
|
||||
/// The public half of the recovery phrase's own Ed25519 key, derived
|
||||
/// from the phrase alone (never the account's signing key, which every
|
||||
/// signed-in device keeps): what proves the phrase. Null for a password
|
||||
/// set up before it was kept: such an account proves no phrase until it
|
||||
/// sets a new one.
|
||||
_idt.ByteData? recoveryProofKey;
|
||||
|
||||
DateTime createdAt;
|
||||
|
||||
DateTime updatedAt;
|
||||
|
|
@ -164,6 +178,7 @@ abstract class AccountPassword
|
|||
_idt.ByteData? passwordKeysCipherText,
|
||||
_idt.ByteData? recoveryKeysNonce,
|
||||
_idt.ByteData? recoveryKeysCipherText,
|
||||
_idt.ByteData? recoveryProofKey,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
});
|
||||
|
|
@ -184,6 +199,8 @@ abstract class AccountPassword
|
|||
'passwordKeysCipherText': passwordKeysCipherText.toJson(),
|
||||
'recoveryKeysNonce': recoveryKeysNonce.toJson(),
|
||||
'recoveryKeysCipherText': recoveryKeysCipherText.toJson(),
|
||||
if (recoveryProofKey != null)
|
||||
'recoveryProofKey': recoveryProofKey?.toJson(),
|
||||
'createdAt': createdAt.toJson(),
|
||||
'updatedAt': updatedAt.toJson(),
|
||||
};
|
||||
|
|
@ -239,6 +256,7 @@ class _AccountPasswordImpl extends AccountPassword {
|
|||
required _idt.ByteData passwordKeysCipherText,
|
||||
required _idt.ByteData recoveryKeysNonce,
|
||||
required _idt.ByteData recoveryKeysCipherText,
|
||||
_idt.ByteData? recoveryProofKey,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
}) : super._(
|
||||
|
|
@ -255,6 +273,7 @@ class _AccountPasswordImpl extends AccountPassword {
|
|||
passwordKeysCipherText: passwordKeysCipherText,
|
||||
recoveryKeysNonce: recoveryKeysNonce,
|
||||
recoveryKeysCipherText: recoveryKeysCipherText,
|
||||
recoveryProofKey: recoveryProofKey,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
);
|
||||
|
|
@ -277,6 +296,7 @@ class _AccountPasswordImpl extends AccountPassword {
|
|||
_idt.ByteData? passwordKeysCipherText,
|
||||
_idt.ByteData? recoveryKeysNonce,
|
||||
_idt.ByteData? recoveryKeysCipherText,
|
||||
Object? recoveryProofKey = _Undefined,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) {
|
||||
|
|
@ -297,6 +317,9 @@ class _AccountPasswordImpl extends AccountPassword {
|
|||
recoveryKeysNonce: recoveryKeysNonce ?? this.recoveryKeysNonce.clone(),
|
||||
recoveryKeysCipherText:
|
||||
recoveryKeysCipherText ?? this.recoveryKeysCipherText.clone(),
|
||||
recoveryProofKey: recoveryProofKey is _idt.ByteData?
|
||||
? recoveryProofKey
|
||||
: this.recoveryProofKey?.clone(),
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
|
|
@ -382,6 +405,13 @@ class AccountPasswordUpdateTable extends _is.UpdateTable<AccountPasswordTable> {
|
|||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> recoveryProofKey(
|
||||
_idt.ByteData? value,
|
||||
) => _is.ColumnValue(
|
||||
table.recoveryProofKey,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<DateTime, DateTime> createdAt(DateTime value) =>
|
||||
_is.ColumnValue(
|
||||
table.createdAt,
|
||||
|
|
@ -447,6 +477,10 @@ class AccountPasswordTable extends _is.Table<int?> {
|
|||
'recoveryKeysCipherText',
|
||||
this,
|
||||
);
|
||||
recoveryProofKey = _is.ColumnByteData(
|
||||
'recoveryProofKey',
|
||||
this,
|
||||
);
|
||||
createdAt = _is.ColumnDateTime(
|
||||
'createdAt',
|
||||
this,
|
||||
|
|
@ -495,6 +529,13 @@ class AccountPasswordTable extends _is.Table<int?> {
|
|||
/// …and what it sealed.
|
||||
late final _is.ColumnByteData recoveryKeysCipherText;
|
||||
|
||||
/// The public half of the recovery phrase's own Ed25519 key, derived
|
||||
/// from the phrase alone (never the account's signing key, which every
|
||||
/// signed-in device keeps): what proves the phrase. Null for a password
|
||||
/// set up before it was kept: such an account proves no phrase until it
|
||||
/// sets a new one.
|
||||
late final _is.ColumnByteData recoveryProofKey;
|
||||
|
||||
late final _is.ColumnDateTime createdAt;
|
||||
|
||||
late final _is.ColumnDateTime updatedAt;
|
||||
|
|
@ -514,6 +555,7 @@ class AccountPasswordTable extends _is.Table<int?> {
|
|||
passwordKeysCipherText,
|
||||
recoveryKeysNonce,
|
||||
recoveryKeysCipherText,
|
||||
recoveryProofKey,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import '../password/wrapped_keys.dart' as _i2rxumte;
|
|||
/// What a device needs to prove the recovery phrase (ADR 0017): the
|
||||
/// account's private keys wrapped under the phrase's key and its public
|
||||
/// keys, which the device opens with the phrase, and a nonce it signs with
|
||||
/// the signing key so opened, within two minutes, once. Alike for an address
|
||||
/// nobody has.
|
||||
/// the phrase's own proof key, within two minutes, once. Alike for an
|
||||
/// address nobody has.
|
||||
abstract class KeyProofChallenge
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
KeyProofChallenge._({
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ abstract class PasswordSetup
|
|||
required this.signingPublicKey,
|
||||
required this.passwordKeys,
|
||||
required this.recoveryKeys,
|
||||
required this.recoveryProofKey,
|
||||
});
|
||||
|
||||
factory PasswordSetup({
|
||||
|
|
@ -41,6 +42,7 @@ abstract class PasswordSetup
|
|||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
required _i2rxumte.WrappedKeys recoveryKeys,
|
||||
required _idt.ByteData recoveryProofKey,
|
||||
}) = _PasswordSetupImpl;
|
||||
|
||||
factory PasswordSetup.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
|
|
@ -64,6 +66,9 @@ abstract class PasswordSetup
|
|||
recoveryKeys: _ibewtqgw.Protocol().deserialize<_i2rxumte.WrappedKeys>(
|
||||
jsonSerialization['recoveryKeys'],
|
||||
),
|
||||
recoveryProofKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['recoveryProofKey'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +87,10 @@ abstract class PasswordSetup
|
|||
|
||||
_i2rxumte.WrappedKeys recoveryKeys;
|
||||
|
||||
/// The public half of the recovery phrase's proof key (Ed25519, from the
|
||||
/// phrase alone).
|
||||
_idt.ByteData recoveryProofKey;
|
||||
|
||||
/// Returns a shallow copy of this [PasswordSetup]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
|
|
@ -93,6 +102,7 @@ abstract class PasswordSetup
|
|||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
_i2rxumte.WrappedKeys? recoveryKeys,
|
||||
_idt.ByteData? recoveryProofKey,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -105,6 +115,7 @@ abstract class PasswordSetup
|
|||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJson(),
|
||||
'recoveryKeys': recoveryKeys.toJson(),
|
||||
'recoveryProofKey': recoveryProofKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +130,7 @@ abstract class PasswordSetup
|
|||
'signingPublicKey': signingPublicKey.toJson(),
|
||||
'passwordKeys': passwordKeys.toJsonForProtocol(),
|
||||
'recoveryKeys': recoveryKeys.toJsonForProtocol(),
|
||||
'recoveryProofKey': recoveryProofKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +149,7 @@ class _PasswordSetupImpl extends PasswordSetup {
|
|||
required _idt.ByteData signingPublicKey,
|
||||
required _i2rxumte.WrappedKeys passwordKeys,
|
||||
required _i2rxumte.WrappedKeys recoveryKeys,
|
||||
required _idt.ByteData recoveryProofKey,
|
||||
}) : super._(
|
||||
salt: salt,
|
||||
costs: costs,
|
||||
|
|
@ -145,6 +158,7 @@ class _PasswordSetupImpl extends PasswordSetup {
|
|||
signingPublicKey: signingPublicKey,
|
||||
passwordKeys: passwordKeys,
|
||||
recoveryKeys: recoveryKeys,
|
||||
recoveryProofKey: recoveryProofKey,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [PasswordSetup]
|
||||
|
|
@ -159,6 +173,7 @@ class _PasswordSetupImpl extends PasswordSetup {
|
|||
_idt.ByteData? signingPublicKey,
|
||||
_i2rxumte.WrappedKeys? passwordKeys,
|
||||
_i2rxumte.WrappedKeys? recoveryKeys,
|
||||
_idt.ByteData? recoveryProofKey,
|
||||
}) {
|
||||
return PasswordSetup(
|
||||
salt: salt ?? this.salt.clone(),
|
||||
|
|
@ -169,6 +184,7 @@ class _PasswordSetupImpl extends PasswordSetup {
|
|||
signingPublicKey: signingPublicKey ?? this.signingPublicKey.clone(),
|
||||
passwordKeys: passwordKeys ?? this.passwordKeys.copyWith(),
|
||||
recoveryKeys: recoveryKeys ?? this.recoveryKeys.copyWith(),
|
||||
recoveryProofKey: recoveryProofKey ?? this.recoveryProofKey.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,6 +251,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
isNullable: false,
|
||||
dartType: 'dart:typed_data:ByteData',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'recoveryProofKey',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: true,
|
||||
dartType: 'dart:typed_data:ByteData?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'createdAt',
|
||||
columnType: _isp.ColumnType.timestampWithoutTimeZone,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ fields:
|
|||
recoveryKeysNonce: ByteData
|
||||
### …and what it sealed.
|
||||
recoveryKeysCipherText: ByteData
|
||||
### The public half of the recovery phrase's own Ed25519 key, derived
|
||||
### from the phrase alone (never the account's signing key, which every
|
||||
### signed-in device keeps): what proves the phrase. Null for a password
|
||||
### set up before it was kept: such an account proves no phrase until it
|
||||
### sets a new one.
|
||||
recoveryProofKey: ByteData?
|
||||
createdAt: DateTime
|
||||
updatedAt: DateTime
|
||||
indexes:
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
### What a device needs to prove the recovery phrase (ADR 0017): the
|
||||
### account's private keys wrapped under the phrase's key and its public
|
||||
### keys, which the device opens with the phrase, and a nonce it signs with
|
||||
### the signing key so opened, within two minutes, once. Alike for an address
|
||||
### nobody has.
|
||||
### the phrase's own proof key, within two minutes, once. Alike for an
|
||||
### address nobody has.
|
||||
class: KeyProofChallenge
|
||||
fields:
|
||||
challenge: UuidValue
|
||||
|
|
|
|||
|
|
@ -84,12 +84,14 @@ Future<void> changePassword(
|
|||
|
||||
/// Replaces the wrapping of [person]'s keys under their recovery phrase
|
||||
/// with [recoveryKeys] (a new phrase, made on the device, which holds the
|
||||
/// keys): the old password's proof for [challenge] (`beginPasswordChange`)
|
||||
/// keys) and the phrase's proof key with [recoveryProofKey] (its public
|
||||
/// half): the old password's proof for [challenge] (`beginPasswordChange`)
|
||||
/// and a [totpCode] or [recoveryCode] from the account, as changing the
|
||||
/// password takes. The old phrase opens nothing any more.
|
||||
/// password takes. The old phrase opens and proves nothing any more.
|
||||
///
|
||||
/// Refused ([PasswordRefusedException]) for wrapped keys out of range
|
||||
/// ([PasswordRefusal.invalidSetup]), and otherwise as [changePassword].
|
||||
/// Refused ([PasswordRefusedException]) for wrapped keys or a proof key out
|
||||
/// of range ([PasswordRefusal.invalidSetup]), and otherwise as
|
||||
/// [changePassword].
|
||||
/// Nothing changes then.
|
||||
Future<void> setRecoveryKeys(
|
||||
Session session, {
|
||||
|
|
@ -100,9 +102,10 @@ Future<void> setRecoveryKeys(
|
|||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
required WrappedKeys recoveryKeys,
|
||||
required ByteData recoveryProofKey,
|
||||
}) async {
|
||||
final keys = requirePasswordKeys(session.serverpod);
|
||||
if (!isWrappedKeys(recoveryKeys)) {
|
||||
if (!isWrappedKeys(recoveryKeys) || !isPublicKey(recoveryProofKey)) {
|
||||
throw refusedFor(PasswordRefusal.invalidSetup);
|
||||
}
|
||||
final row = await _proveOwner(
|
||||
|
|
@ -116,12 +119,14 @@ Future<void> setRecoveryKeys(
|
|||
recoveryCode: recoveryCode,
|
||||
);
|
||||
// UPDATE "account_password" SET "recoveryKeysNonce" = $1,
|
||||
// "recoveryKeysCipherText" = $2, "updatedAt" = $3 WHERE "id" = $4
|
||||
// "recoveryKeysCipherText" = $2, "recoveryProofKey" = $3,
|
||||
// "updatedAt" = $4 WHERE "id" = $5
|
||||
await AccountPassword.db.updateRow(
|
||||
session,
|
||||
row.copyWith(
|
||||
recoveryKeysNonce: recoveryKeys.nonce,
|
||||
recoveryKeysCipherText: recoveryKeys.cipherText,
|
||||
recoveryProofKey: recoveryProofKey,
|
||||
updatedAt: clock.now().toUtc(),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ class PasswordEndpoint extends Endpoint {
|
|||
}
|
||||
|
||||
/// A new recovery phrase: [recoveryKeys], the caller's keys wrapped under
|
||||
/// it on the device, replace the old wrapping, with the password's proof
|
||||
/// it on the device, replace the old wrapping, and [recoveryProofKey], the
|
||||
/// public half of its proof key, the old one, with the password's proof
|
||||
/// for [challenge] ([beginChange]) and a [totpCode] or [recoveryCode], as
|
||||
/// [change] takes. Refusals as `setRecoveryKeys`'.
|
||||
Future<void> setRecoveryKeys(
|
||||
|
|
@ -90,6 +91,7 @@ class PasswordEndpoint extends Endpoint {
|
|||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
required WrappedKeys recoveryKeys,
|
||||
required ByteData recoveryProofKey,
|
||||
}) => AccountRegister.of(session.serverpod).setRecoveryKeys(
|
||||
session,
|
||||
person: session.authenticated!.authUserId,
|
||||
|
|
@ -99,5 +101,6 @@ class PasswordEndpoint extends Endpoint {
|
|||
totpCode: totpCode,
|
||||
recoveryCode: recoveryCode,
|
||||
recoveryKeys: recoveryKeys,
|
||||
recoveryProofKey: recoveryProofKey,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,3 +13,6 @@ fields:
|
|||
signingPublicKey: ByteData
|
||||
passwordKeys: WrappedKeys
|
||||
recoveryKeys: WrappedKeys
|
||||
### The public half of the recovery phrase's proof key (Ed25519, from the
|
||||
### phrase alone).
|
||||
recoveryProofKey: ByteData
|
||||
|
|
|
|||
|
|
@ -202,6 +202,9 @@ bool _areWrappedKeys(WrappedKeys keys) =>
|
|||
/// a nonce and both keys sealed, of their lengths.
|
||||
bool isWrappedKeys(WrappedKeys keys) => _areWrappedKeys(keys);
|
||||
|
||||
/// Whether [key] is as long as an X25519 or Ed25519 public key.
|
||||
bool isPublicKey(ByteData key) => key.lengthInBytes == _publicKeyLength;
|
||||
|
||||
/// Whether [verifier] is SRP's v, padded to N's length: 1 < v < N. Outside,
|
||||
/// it would make every password right, or none.
|
||||
bool _isVerifier(ByteData verifier) {
|
||||
|
|
@ -220,7 +223,8 @@ bool isPasswordSetup(PasswordSetup setup) =>
|
|||
setup.encryptionPublicKey.lengthInBytes == _publicKeyLength &&
|
||||
setup.signingPublicKey.lengthInBytes == _publicKeyLength &&
|
||||
_areWrappedKeys(setup.passwordKeys) &&
|
||||
_areWrappedKeys(setup.recoveryKeys);
|
||||
_areWrappedKeys(setup.recoveryKeys) &&
|
||||
setup.recoveryProofKey.lengthInBytes == _publicKeyLength;
|
||||
|
||||
/// Whether [password] is a new password this register takes.
|
||||
bool isNewPassword(NewPassword password) =>
|
||||
|
|
@ -250,6 +254,7 @@ AccountPassword passwordRowOf(
|
|||
passwordKeysCipherText: setup.passwordKeys.cipherText,
|
||||
recoveryKeysNonce: setup.recoveryKeys.nonce,
|
||||
recoveryKeysCipherText: setup.recoveryKeys.cipherText,
|
||||
recoveryProofKey: setup.recoveryProofKey,
|
||||
createdAt: row?.createdAt ?? now,
|
||||
updatedAt: now,
|
||||
);
|
||||
|
|
@ -967,11 +972,13 @@ Future<KeyProofChallenge> beginKeyProof(
|
|||
}
|
||||
|
||||
/// Takes the key proof [challenge] (once) and checks [signature], the
|
||||
/// account's Ed25519 signature over [keyProofMessage]; the account's
|
||||
/// password row. Refused ([PasswordRefusedException]) for a challenge
|
||||
/// unknown, used or too old ([PasswordRefusal.challengeExpired]), and for a
|
||||
/// signature that isn't the account's or an address nobody has
|
||||
/// ([PasswordRefusal.wrongCredentials]).
|
||||
/// signature over [keyProofMessage] by the recovery phrase's own proof key
|
||||
/// (`recoveryProofKey`: derived from the phrase alone, so the account's
|
||||
/// signing key, which every signed-in device keeps, proves nothing); the
|
||||
/// account's password row. Refused ([PasswordRefusedException]) for a
|
||||
/// challenge unknown, used or too old ([PasswordRefusal.challengeExpired]),
|
||||
/// and for a signature by another key, an account that keeps no proof key,
|
||||
/// or an address nobody has ([PasswordRefusal.wrongCredentials]).
|
||||
Future<AccountPassword> proveRecoveryPhrase(
|
||||
Session session,
|
||||
PasswordKeys keys, {
|
||||
|
|
@ -983,18 +990,19 @@ Future<AccountPassword> proveRecoveryPhrase(
|
|||
challenge,
|
||||
SignInChallengePurpose.keyProof,
|
||||
);
|
||||
// Checked against the decoy's key for an address nobody has, so it takes
|
||||
// as long.
|
||||
// Checked against the decoy's key for an address nobody has (or an
|
||||
// account keeping no proof key), so it takes as long.
|
||||
final decoy = await _decoyKeys(keys.decoyKey, kept.addressHash);
|
||||
final password = await _passwordOf(session, kept.authUserId);
|
||||
final proofKey = password?.recoveryProofKey;
|
||||
final signed = await isAccountSignature(
|
||||
keyProofMessage(challenge, _unhex(kept.secret)),
|
||||
signature: Uint8List.sublistView(signature),
|
||||
publicKey: password == null
|
||||
? decoy.signingPublicKey
|
||||
: Uint8List.sublistView(password.signingPublicKey),
|
||||
publicKey: proofKey == null
|
||||
? decoy.recoveryProofKey
|
||||
: Uint8List.sublistView(proofKey),
|
||||
);
|
||||
if (!signed || password == null) {
|
||||
if (!signed || password == null || proofKey == null) {
|
||||
throw refusedFor(PasswordRefusal.wrongCredentials);
|
||||
}
|
||||
return password;
|
||||
|
|
@ -1079,14 +1087,16 @@ Uint8List _decoyBytes(
|
|||
|
||||
/// An address nobody has's keys, as a key proof's first step shows them:
|
||||
/// an X25519 public key (its top bit clear, as a point's u is), an Ed25519
|
||||
/// one made from a seed, and wrapped keys of an account's length. Computed
|
||||
/// for every proof begun, an account's too.
|
||||
/// one made from a seed, and wrapped keys of an account's length; and the
|
||||
/// phrase's proof key its second step checks against. Computed for every
|
||||
/// proof begun, an account's too.
|
||||
Future<
|
||||
({
|
||||
Uint8List encryptionPublicKey,
|
||||
Uint8List signingPublicKey,
|
||||
Uint8List recoveryKeysNonce,
|
||||
Uint8List recoveryKeysCipherText,
|
||||
Uint8List recoveryProofKey,
|
||||
})
|
||||
>
|
||||
_decoyKeys(String decoyKey, String hash) async => (
|
||||
|
|
@ -1111,6 +1121,9 @@ _decoyKeys(String decoyKey, String hash) async => (
|
|||
hash,
|
||||
wrappedKeysLength,
|
||||
),
|
||||
recoveryProofKey: await ed25519PublicKeyFromSeed(
|
||||
_decoyBytes(decoyKey, 'proof seed', hash, 32),
|
||||
),
|
||||
);
|
||||
|
||||
/// [length] bytes from the system's secure random source.
|
||||
|
|
|
|||
|
|
@ -300,8 +300,9 @@ abstract interface class AccountRegister {
|
|||
UuidValue? keepSession,
|
||||
});
|
||||
|
||||
/// Replaces the wrapping of [person]'s keys under their recovery phrase,
|
||||
/// with the password's proof and a code. Refusals as `setRecoveryKeys`'.
|
||||
/// Replaces the wrapping of [person]'s keys under their recovery phrase
|
||||
/// and the phrase's proof key, with the password's proof and a code.
|
||||
/// Refusals as `setRecoveryKeys`'.
|
||||
Future<void> setRecoveryKeys(
|
||||
Session session, {
|
||||
required UuidValue person,
|
||||
|
|
@ -311,6 +312,7 @@ abstract interface class AccountRegister {
|
|||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
required WrappedKeys recoveryKeys,
|
||||
required ByteData recoveryProofKey,
|
||||
});
|
||||
|
||||
/// Deletes the account [person] with everything the register's server has
|
||||
|
|
|
|||
|
|
@ -366,6 +366,7 @@ Future<Map<String, Object?>?> _answer(
|
|||
totpCode: arguments.optionalString('totpCode'),
|
||||
recoveryCode: arguments.optionalString('recoveryCode'),
|
||||
recoveryKeys: arguments.model('recoveryKeys', WrappedKeys.fromJson),
|
||||
recoveryProofKey: arguments.bytes('recoveryProofKey'),
|
||||
);
|
||||
return const {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -626,6 +626,7 @@ final class LocalRegister implements AccountRegister {
|
|||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
required WrappedKeys recoveryKeys,
|
||||
required ByteData recoveryProofKey,
|
||||
}) => change.setRecoveryKeys(
|
||||
session,
|
||||
person: person,
|
||||
|
|
@ -635,6 +636,7 @@ final class LocalRegister implements AccountRegister {
|
|||
totpCode: totpCode,
|
||||
recoveryCode: recoveryCode,
|
||||
recoveryKeys: recoveryKeys,
|
||||
recoveryProofKey: recoveryProofKey,
|
||||
);
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -590,6 +590,7 @@ final class RemoteRegister implements AccountRegister {
|
|||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
required WrappedKeys recoveryKeys,
|
||||
required ByteData recoveryProofKey,
|
||||
}) => _ask(RegisterOperation.setRecoveryKeys, {
|
||||
'person': person.uuid,
|
||||
'challenge': challenge.uuid,
|
||||
|
|
@ -598,6 +599,7 @@ final class RemoteRegister implements AccountRegister {
|
|||
'totpCode': totpCode,
|
||||
'recoveryCode': recoveryCode,
|
||||
'recoveryKeys': recoveryKeys.toJson(),
|
||||
'recoveryProofKey': _bytes(recoveryProofKey),
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,979 @@
|
|||
BEGIN;
|
||||
|
||||
--
|
||||
-- Function: gen_random_uuid_v7()
|
||||
-- Source: https://gist.github.com/kjmph/5bd772b2c2df145aa645b837da7eca74
|
||||
-- License: MIT (copyright notice included on the generator source code).
|
||||
--
|
||||
create or replace function gen_random_uuid_v7()
|
||||
returns uuid
|
||||
as $$
|
||||
begin
|
||||
-- use random v4 uuid as starting point (which has the same variant we need)
|
||||
-- then overlay timestamp
|
||||
-- then set version 7 by flipping the 2 and 1 bit in the version 4 string
|
||||
return encode(
|
||||
set_bit(
|
||||
set_bit(
|
||||
overlay(uuid_send(gen_random_uuid())
|
||||
placing substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3)
|
||||
from 1 for 6
|
||||
),
|
||||
52, 1
|
||||
),
|
||||
53, 1
|
||||
),
|
||||
'hex')::uuid;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
volatile;
|
||||
|
||||
--
|
||||
-- Class AccountDeletionNotice as table account_deletion_notice
|
||||
--
|
||||
CREATE TABLE "account_deletion_notice" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"account" uuid NOT NULL,
|
||||
"member" text NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL,
|
||||
"attempts" bigint NOT NULL,
|
||||
"lastAttemptAt" timestamp without time zone
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "account_deletion_notice_idx" ON "account_deletion_notice" USING btree ("account", "member");
|
||||
|
||||
--
|
||||
-- Class AccountPassword as table account_password
|
||||
--
|
||||
CREATE TABLE "account_password" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"authUserId" uuid NOT NULL,
|
||||
"salt" bytea NOT NULL,
|
||||
"kdfAlgorithm" text NOT NULL,
|
||||
"kdfOpsLimit" bigint NOT NULL,
|
||||
"kdfMemLimit" bigint NOT NULL,
|
||||
"verifier" bytea NOT NULL,
|
||||
"encryptionPublicKey" bytea NOT NULL,
|
||||
"signingPublicKey" bytea NOT NULL,
|
||||
"passwordKeysNonce" bytea NOT NULL,
|
||||
"passwordKeysCipherText" bytea NOT NULL,
|
||||
"recoveryKeysNonce" bytea NOT NULL,
|
||||
"recoveryKeysCipherText" bytea NOT NULL,
|
||||
"recoveryProofKey" bytea,
|
||||
"createdAt" timestamp without time zone NOT NULL,
|
||||
"updatedAt" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "account_password_person_idx" ON "account_password" USING btree ("authUserId");
|
||||
|
||||
--
|
||||
-- Class AccountReset as table account_reset
|
||||
--
|
||||
CREATE TABLE "account_reset" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"authUserId" uuid NOT NULL,
|
||||
"kind" text NOT NULL,
|
||||
"takesEffectAt" timestamp without time zone NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "account_reset_person_kind_idx" ON "account_reset" USING btree ("authUserId", "kind");
|
||||
|
||||
--
|
||||
-- Class EmailCodeRequest as table email_code_request
|
||||
--
|
||||
CREATE TABLE "email_code_request" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"addressHash" text NOT NULL,
|
||||
"challengeId" uuid NOT NULL,
|
||||
"switchChallengeId" uuid,
|
||||
"createdAt" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "email_code_request_address_idx" ON "email_code_request" USING btree ("addressHash");
|
||||
|
||||
--
|
||||
-- Class EmailIdentity as table email_identity
|
||||
--
|
||||
CREATE TABLE "email_identity" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"authUserId" uuid NOT NULL,
|
||||
"addressHash" text NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "email_identity_person_idx" ON "email_identity" USING btree ("authUserId");
|
||||
CREATE UNIQUE INDEX "email_identity_address_idx" ON "email_identity" USING btree ("addressHash");
|
||||
|
||||
--
|
||||
-- Class PublicProfile as table public_profile
|
||||
--
|
||||
CREATE TABLE "public_profile" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"authUserId" uuid NOT NULL,
|
||||
"handle" text NOT NULL,
|
||||
"handleKey" text NOT NULL,
|
||||
"displayName" text NOT NULL,
|
||||
"bio" text NOT NULL,
|
||||
"links" json NOT NULL,
|
||||
"verifiedLinks" json,
|
||||
"avatarHash" text,
|
||||
"firstPublishedAt" timestamp without time zone,
|
||||
"updatedAt" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "public_profile_person_idx" ON "public_profile" USING btree ("authUserId");
|
||||
CREATE UNIQUE INDEX "public_profile_handle_idx" ON "public_profile" USING btree ("handleKey");
|
||||
|
||||
--
|
||||
-- Class RecoveryCode as table recovery_code
|
||||
--
|
||||
CREATE TABLE "recovery_code" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"authUserId" uuid NOT NULL,
|
||||
"hash" text NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "recovery_code_person_idx" ON "recovery_code" USING btree ("authUserId");
|
||||
|
||||
--
|
||||
-- Class SessionsEndedNotice as table sessions_ended_notice
|
||||
--
|
||||
CREATE TABLE "sessions_ended_notice" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"account" uuid NOT NULL,
|
||||
"member" text NOT NULL,
|
||||
"endedBefore" timestamp without time zone NOT NULL,
|
||||
"keepSession" uuid,
|
||||
"attempts" bigint NOT NULL,
|
||||
"lastAttemptAt" timestamp without time zone
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "sessions_ended_notice_idx" ON "sessions_ended_notice" USING btree ("account", "member");
|
||||
|
||||
--
|
||||
-- Class SignInChallenge as table sign_in_challenge
|
||||
--
|
||||
CREATE TABLE "sign_in_challenge" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid,
|
||||
"purpose" text NOT NULL,
|
||||
"addressHash" text NOT NULL,
|
||||
"secret" text NOT NULL,
|
||||
"serverValue" text,
|
||||
"createdAt" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "sign_in_challenge_created_idx" ON "sign_in_challenge" USING btree ("createdAt");
|
||||
|
||||
--
|
||||
-- Class TotpAuthenticator as table totp_authenticator
|
||||
--
|
||||
CREATE TABLE "totp_authenticator" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"secret" bytea NOT NULL,
|
||||
"confirmedAt" timestamp without time zone,
|
||||
"lastStep" bigint,
|
||||
"createdAt" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "totp_authenticator_person_idx" ON "totp_authenticator" USING btree ("authUserId");
|
||||
|
||||
--
|
||||
-- Class CloudStorageEntry as table serverpod_cloud_storage
|
||||
--
|
||||
CREATE TABLE "serverpod_cloud_storage" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"storageId" text NOT NULL,
|
||||
"path" text NOT NULL,
|
||||
"addedTime" timestamp without time zone NOT NULL,
|
||||
"expiration" timestamp without time zone,
|
||||
"byteData" bytea NOT NULL,
|
||||
"verified" boolean NOT NULL,
|
||||
"contentType" text,
|
||||
"cacheControl" text,
|
||||
"contentDisposition" text,
|
||||
"contentEncoding" text,
|
||||
"customMetadata" text
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_cloud_storage_path_idx" ON "serverpod_cloud_storage" USING btree ("storageId", "path");
|
||||
CREATE INDEX "serverpod_cloud_storage_expiration" ON "serverpod_cloud_storage" USING btree ("expiration");
|
||||
|
||||
--
|
||||
-- Class CloudStorageDirectDownloadEntry as table serverpod_cloud_storage_direct_download
|
||||
--
|
||||
CREATE TABLE "serverpod_cloud_storage_direct_download" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"storageId" text NOT NULL,
|
||||
"path" text NOT NULL,
|
||||
"expiration" timestamp without time zone NOT NULL,
|
||||
"authKey" text NOT NULL,
|
||||
"downloadFileName" text,
|
||||
"contentType" text
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_cloud_storage_direct_download_auth_key" ON "serverpod_cloud_storage_direct_download" USING btree ("authKey");
|
||||
CREATE INDEX "serverpod_cloud_storage_direct_download_expiration" ON "serverpod_cloud_storage_direct_download" USING btree ("expiration");
|
||||
|
||||
--
|
||||
-- Class CloudStorageDirectUploadEntry as table serverpod_cloud_storage_direct_upload
|
||||
--
|
||||
CREATE TABLE "serverpod_cloud_storage_direct_upload" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"storageId" text NOT NULL,
|
||||
"path" text NOT NULL,
|
||||
"expiration" timestamp without time zone NOT NULL,
|
||||
"authKey" text NOT NULL,
|
||||
"maxFileSize" bigint NOT NULL DEFAULT 10485760,
|
||||
"contentLength" bigint,
|
||||
"preventOverwrite" boolean NOT NULL DEFAULT false,
|
||||
"contentType" text,
|
||||
"cacheControl" text,
|
||||
"contentDisposition" text,
|
||||
"contentEncoding" text,
|
||||
"customMetadata" text
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_cloud_storage_direct_upload_storage_path" ON "serverpod_cloud_storage_direct_upload" USING btree ("storageId", "path");
|
||||
|
||||
--
|
||||
-- Class FutureCallEntry as table serverpod_future_call
|
||||
--
|
||||
CREATE TABLE "serverpod_future_call" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"name" text NOT NULL,
|
||||
"time" timestamp without time zone NOT NULL,
|
||||
"serializedObject" text,
|
||||
"serverId" text NOT NULL,
|
||||
"identifier" text,
|
||||
"scheduling" json
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "serverpod_future_call_time_idx" ON "serverpod_future_call" USING btree ("time");
|
||||
CREATE INDEX "serverpod_future_call_serverId_idx" ON "serverpod_future_call" USING btree ("serverId");
|
||||
CREATE INDEX "serverpod_future_call_identifier_idx" ON "serverpod_future_call" USING btree ("identifier");
|
||||
|
||||
--
|
||||
-- Class FutureCallClaimEntry as table serverpod_future_call_claim
|
||||
--
|
||||
CREATE TABLE "serverpod_future_call_claim" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"futureCallId" bigint,
|
||||
"lastHeartbeatTime" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "future_call_unique_idx" ON "serverpod_future_call_claim" USING btree ("futureCallId");
|
||||
|
||||
--
|
||||
-- Class ServerHealthConnectionInfo as table serverpod_health_connection_info
|
||||
--
|
||||
CREATE TABLE "serverpod_health_connection_info" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"serverId" text NOT NULL,
|
||||
"timestamp" timestamp without time zone NOT NULL,
|
||||
"active" bigint NOT NULL,
|
||||
"closing" bigint NOT NULL,
|
||||
"idle" bigint NOT NULL,
|
||||
"granularity" bigint NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_health_connection_info_timestamp_idx" ON "serverpod_health_connection_info" USING btree ("timestamp", "serverId", "granularity");
|
||||
|
||||
--
|
||||
-- Class ServerHealthMetric as table serverpod_health_metric
|
||||
--
|
||||
CREATE TABLE "serverpod_health_metric" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"name" text NOT NULL,
|
||||
"serverId" text NOT NULL,
|
||||
"timestamp" timestamp without time zone NOT NULL,
|
||||
"isHealthy" boolean NOT NULL,
|
||||
"value" double precision NOT NULL,
|
||||
"granularity" bigint NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_health_metric_timestamp_idx" ON "serverpod_health_metric" USING btree ("timestamp", "serverId", "name", "granularity");
|
||||
|
||||
--
|
||||
-- Class LogEntry as table serverpod_log
|
||||
--
|
||||
CREATE TABLE "serverpod_log" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"sessionLogId" bigint NOT NULL,
|
||||
"messageId" bigint,
|
||||
"reference" text,
|
||||
"serverId" text NOT NULL,
|
||||
"time" timestamp without time zone NOT NULL,
|
||||
"logLevel" bigint NOT NULL,
|
||||
"message" text NOT NULL,
|
||||
"error" text,
|
||||
"stackTrace" text,
|
||||
"order" bigint NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "serverpod_log_sessionLogId_idx" ON "serverpod_log" USING btree ("sessionLogId", "order");
|
||||
|
||||
--
|
||||
-- Class MessageLogEntry as table serverpod_message_log
|
||||
--
|
||||
CREATE TABLE "serverpod_message_log" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"sessionLogId" bigint NOT NULL,
|
||||
"serverId" text NOT NULL,
|
||||
"messageId" bigint NOT NULL,
|
||||
"endpoint" text NOT NULL,
|
||||
"messageName" text NOT NULL,
|
||||
"duration" double precision NOT NULL,
|
||||
"error" text,
|
||||
"stackTrace" text,
|
||||
"slow" boolean NOT NULL,
|
||||
"order" bigint NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "serverpod_message_log_sessionLogId_idx" ON "serverpod_message_log" USING btree ("sessionLogId", "order");
|
||||
|
||||
--
|
||||
-- Class MethodInfo as table serverpod_method
|
||||
--
|
||||
CREATE TABLE "serverpod_method" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"endpoint" text NOT NULL,
|
||||
"method" text NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_method_endpoint_method_idx" ON "serverpod_method" USING btree ("endpoint", "method");
|
||||
|
||||
--
|
||||
-- Class DatabaseMigrationVersion as table serverpod_migrations
|
||||
--
|
||||
CREATE TABLE "serverpod_migrations" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"module" text NOT NULL,
|
||||
"version" text NOT NULL,
|
||||
"timestamp" timestamp without time zone
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_migrations_ids" ON "serverpod_migrations" USING btree ("module");
|
||||
|
||||
--
|
||||
-- Class QueryLogEntry as table serverpod_query_log
|
||||
--
|
||||
CREATE TABLE "serverpod_query_log" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"serverId" text NOT NULL,
|
||||
"sessionLogId" bigint NOT NULL,
|
||||
"messageId" bigint,
|
||||
"query" text NOT NULL,
|
||||
"duration" double precision NOT NULL,
|
||||
"numRows" bigint,
|
||||
"error" text,
|
||||
"stackTrace" text,
|
||||
"slow" boolean NOT NULL,
|
||||
"order" bigint NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "serverpod_query_log_sessionLogId_idx" ON "serverpod_query_log" USING btree ("sessionLogId", "order");
|
||||
|
||||
--
|
||||
-- Class ReadWriteTestEntry as table serverpod_readwrite_test
|
||||
--
|
||||
CREATE TABLE "serverpod_readwrite_test" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"number" bigint NOT NULL
|
||||
);
|
||||
|
||||
--
|
||||
-- Class RuntimeSettings as table serverpod_runtime_settings
|
||||
--
|
||||
CREATE TABLE "serverpod_runtime_settings" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"logSettings" json NOT NULL,
|
||||
"logSettingsOverrides" json NOT NULL,
|
||||
"logServiceCalls" boolean NOT NULL,
|
||||
"logMalformedCalls" boolean NOT NULL
|
||||
);
|
||||
|
||||
--
|
||||
-- Class SessionLogEntry as table serverpod_session_log
|
||||
--
|
||||
CREATE TABLE "serverpod_session_log" (
|
||||
"id" bigserial PRIMARY KEY,
|
||||
"serverId" text NOT NULL,
|
||||
"time" timestamp without time zone NOT NULL,
|
||||
"module" text,
|
||||
"endpoint" text,
|
||||
"method" text,
|
||||
"duration" double precision,
|
||||
"numQueries" bigint,
|
||||
"slow" boolean,
|
||||
"error" text,
|
||||
"stackTrace" text,
|
||||
"authenticatedUserId" bigint,
|
||||
"userId" text,
|
||||
"isOpen" boolean,
|
||||
"touched" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "serverpod_session_log_serverid_idx" ON "serverpod_session_log" USING btree ("serverId");
|
||||
CREATE INDEX "serverpod_session_log_time_idx" ON "serverpod_session_log" USING btree ("time");
|
||||
CREATE INDEX "serverpod_session_log_touched_idx" ON "serverpod_session_log" USING btree ("touched");
|
||||
CREATE INDEX "serverpod_session_log_isopen_idx" ON "serverpod_session_log" USING btree ("isOpen");
|
||||
|
||||
--
|
||||
-- Class AnonymousAccount as table serverpod_auth_idp_anonymous_account
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_anonymous_account" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
--
|
||||
-- Class AppleAccount as table serverpod_auth_idp_apple_account
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_apple_account" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"userIdentifier" text NOT NULL,
|
||||
"refreshToken" text NOT NULL,
|
||||
"refreshTokenRequestedWithBundleIdentifier" boolean NOT NULL,
|
||||
"lastRefreshedAt" timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"authUserId" uuid NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL,
|
||||
"email" text,
|
||||
"isEmailVerified" boolean,
|
||||
"isPrivateEmail" boolean,
|
||||
"firstName" text,
|
||||
"lastName" text
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_apple_account_identifier" ON "serverpod_auth_idp_apple_account" USING btree ("userIdentifier");
|
||||
|
||||
--
|
||||
-- Class EmailAccount as table serverpod_auth_idp_email_account
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_email_account" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL,
|
||||
"email" text NOT NULL,
|
||||
"passwordHash" text NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_idp_email_account_email" ON "serverpod_auth_idp_email_account" USING btree ("email");
|
||||
|
||||
--
|
||||
-- Class EmailAccountPasswordResetRequest as table serverpod_auth_idp_email_account_password_reset_request
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_email_account_password_reset_request" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"emailAccountId" uuid NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"challengeId" uuid NOT NULL,
|
||||
"setPasswordChallengeId" uuid
|
||||
);
|
||||
|
||||
--
|
||||
-- Class EmailAccountRequest as table serverpod_auth_idp_email_account_request
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_email_account_request" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"createdAt" timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"email" text NOT NULL,
|
||||
"challengeId" uuid NOT NULL,
|
||||
"createAccountChallengeId" uuid
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_idp_email_account_request_email" ON "serverpod_auth_idp_email_account_request" USING btree ("email");
|
||||
|
||||
--
|
||||
-- Class FacebookAccount as table serverpod_auth_idp_facebook_account
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_facebook_account" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL,
|
||||
"userIdentifier" text NOT NULL,
|
||||
"email" text,
|
||||
"fullName" text,
|
||||
"firstName" text,
|
||||
"lastName" text
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_facebook_account_user_identifier" ON "serverpod_auth_idp_facebook_account" USING btree ("userIdentifier");
|
||||
|
||||
--
|
||||
-- Class FirebaseAccount as table serverpod_auth_idp_firebase_account
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_firebase_account" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"created" timestamp without time zone NOT NULL,
|
||||
"email" text,
|
||||
"phone" text,
|
||||
"userIdentifier" text NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_firebase_account_user_identifier" ON "serverpod_auth_idp_firebase_account" USING btree ("userIdentifier");
|
||||
|
||||
--
|
||||
-- Class GitHubAccount as table serverpod_auth_idp_github_account
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_github_account" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"userIdentifier" text NOT NULL,
|
||||
"email" text,
|
||||
"created" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_github_account_user_identifier" ON "serverpod_auth_idp_github_account" USING btree ("userIdentifier");
|
||||
|
||||
--
|
||||
-- Class GoogleAccount as table serverpod_auth_idp_google_account
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_google_account" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"created" timestamp without time zone NOT NULL,
|
||||
"email" text NOT NULL,
|
||||
"userIdentifier" text NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_google_account_user_identifier" ON "serverpod_auth_idp_google_account" USING btree ("userIdentifier");
|
||||
|
||||
--
|
||||
-- Class MicrosoftAccount as table serverpod_auth_idp_microsoft_account
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_microsoft_account" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"userIdentifier" text NOT NULL,
|
||||
"email" text,
|
||||
"created" timestamp without time zone NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_microsoft_account_user_identifier" ON "serverpod_auth_idp_microsoft_account" USING btree ("userIdentifier");
|
||||
|
||||
--
|
||||
-- Class PasskeyAccount as table serverpod_auth_idp_passkey_account
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_passkey_account" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL,
|
||||
"keyId" bytea NOT NULL,
|
||||
"keyIdBase64" text NOT NULL,
|
||||
"clientDataJSON" bytea NOT NULL,
|
||||
"attestationObject" bytea NOT NULL,
|
||||
"originalChallenge" bytea NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_idp_passkey_account_key_id_base64" ON "serverpod_auth_idp_passkey_account" USING btree ("keyIdBase64");
|
||||
|
||||
--
|
||||
-- Class PasskeyChallenge as table serverpod_auth_idp_passkey_challenge
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_passkey_challenge" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"createdAt" timestamp without time zone NOT NULL,
|
||||
"challenge" bytea NOT NULL
|
||||
);
|
||||
|
||||
--
|
||||
-- Class RateLimitedRequestAttempt as table serverpod_auth_idp_rate_limited_request_attempt
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_rate_limited_request_attempt" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"domain" text NOT NULL,
|
||||
"source" text NOT NULL,
|
||||
"key" text NOT NULL,
|
||||
"ipAddress" text,
|
||||
"attemptedAt" timestamp without time zone NOT NULL,
|
||||
"extraData" json
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "serverpod_auth_idp_rate_limited_request_attempt_composite" ON "serverpod_auth_idp_rate_limited_request_attempt" USING btree ("domain", "source", "key", "attemptedAt");
|
||||
|
||||
--
|
||||
-- Class SecretChallenge as table serverpod_auth_idp_secret_challenge
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_idp_secret_challenge" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"challengeCodeHash" text NOT NULL
|
||||
);
|
||||
|
||||
--
|
||||
-- Class RefreshToken as table serverpod_auth_core_jwt_refresh_token
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_core_jwt_refresh_token" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"scopeNames" json NOT NULL,
|
||||
"extraClaims" text,
|
||||
"method" text NOT NULL,
|
||||
"fixedSecret" bytea NOT NULL,
|
||||
"rotatingSecretHash" text NOT NULL,
|
||||
"lastUpdatedAt" timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"createdAt" timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "serverpod_auth_core_jwt_refresh_token_last_updated_at" ON "serverpod_auth_core_jwt_refresh_token" USING btree ("lastUpdatedAt");
|
||||
|
||||
--
|
||||
-- Class UserProfile as table serverpod_auth_core_profile
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_core_profile" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"userName" text,
|
||||
"fullName" text,
|
||||
"email" text,
|
||||
"createdAt" timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"imageId" uuid
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE UNIQUE INDEX "serverpod_auth_profile_user_profile_email_auth_user_id" ON "serverpod_auth_core_profile" USING btree ("authUserId");
|
||||
|
||||
--
|
||||
-- Class UserProfileImage as table serverpod_auth_core_profile_image
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_core_profile_image" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"userProfileId" uuid NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"storageId" text NOT NULL,
|
||||
"path" text NOT NULL,
|
||||
"url" text NOT NULL
|
||||
);
|
||||
|
||||
--
|
||||
-- Class ServerSideSession as table serverpod_auth_core_session
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_core_session" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"authUserId" uuid NOT NULL,
|
||||
"scopeNames" json NOT NULL,
|
||||
"createdAt" timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"lastUsedAt" timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"expiresAt" timestamp without time zone,
|
||||
"expireAfterUnusedFor" bigint,
|
||||
"sessionKeyHash" bytea NOT NULL,
|
||||
"sessionKeySalt" bytea NOT NULL,
|
||||
"method" text NOT NULL
|
||||
);
|
||||
|
||||
--
|
||||
-- Class AuthUser as table serverpod_auth_core_user
|
||||
--
|
||||
CREATE TABLE "serverpod_auth_core_user" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"createdAt" timestamp without time zone NOT NULL,
|
||||
"scopeNames" json NOT NULL,
|
||||
"blocked" boolean NOT NULL
|
||||
);
|
||||
|
||||
--
|
||||
-- Foreign relations for "email_code_request" table
|
||||
--
|
||||
ALTER TABLE ONLY "email_code_request"
|
||||
ADD CONSTRAINT "email_code_request_fk_0"
|
||||
FOREIGN KEY("challengeId")
|
||||
REFERENCES "serverpod_auth_idp_secret_challenge"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
ALTER TABLE ONLY "email_code_request"
|
||||
ADD CONSTRAINT "email_code_request_fk_1"
|
||||
FOREIGN KEY("switchChallengeId")
|
||||
REFERENCES "serverpod_auth_idp_secret_challenge"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_future_call_claim" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_future_call_claim"
|
||||
ADD CONSTRAINT "serverpod_future_call_claim_fk_0"
|
||||
FOREIGN KEY("futureCallId")
|
||||
REFERENCES "serverpod_future_call"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_log" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_log"
|
||||
ADD CONSTRAINT "serverpod_log_fk_0"
|
||||
FOREIGN KEY("sessionLogId")
|
||||
REFERENCES "serverpod_session_log"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_message_log" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_message_log"
|
||||
ADD CONSTRAINT "serverpod_message_log_fk_0"
|
||||
FOREIGN KEY("sessionLogId")
|
||||
REFERENCES "serverpod_session_log"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_query_log" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_query_log"
|
||||
ADD CONSTRAINT "serverpod_query_log_fk_0"
|
||||
FOREIGN KEY("sessionLogId")
|
||||
REFERENCES "serverpod_session_log"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_anonymous_account" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_anonymous_account"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_anonymous_account_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_apple_account" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_apple_account"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_apple_account_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_email_account" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_email_account"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_email_account_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_email_account_password_reset_request" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_email_account_password_reset_request"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_email_account_password_reset_request_fk_0"
|
||||
FOREIGN KEY("emailAccountId")
|
||||
REFERENCES "serverpod_auth_idp_email_account"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_email_account_password_reset_request"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_email_account_password_reset_request_fk_1"
|
||||
FOREIGN KEY("challengeId")
|
||||
REFERENCES "serverpod_auth_idp_secret_challenge"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_email_account_password_reset_request"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_email_account_password_reset_request_fk_2"
|
||||
FOREIGN KEY("setPasswordChallengeId")
|
||||
REFERENCES "serverpod_auth_idp_secret_challenge"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_email_account_request" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_email_account_request"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_email_account_request_fk_0"
|
||||
FOREIGN KEY("challengeId")
|
||||
REFERENCES "serverpod_auth_idp_secret_challenge"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_email_account_request"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_email_account_request_fk_1"
|
||||
FOREIGN KEY("createAccountChallengeId")
|
||||
REFERENCES "serverpod_auth_idp_secret_challenge"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_facebook_account" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_facebook_account"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_facebook_account_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_firebase_account" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_firebase_account"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_firebase_account_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_github_account" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_github_account"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_github_account_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_google_account" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_google_account"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_google_account_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_microsoft_account" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_microsoft_account"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_microsoft_account_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_idp_passkey_account" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_idp_passkey_account"
|
||||
ADD CONSTRAINT "serverpod_auth_idp_passkey_account_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_core_jwt_refresh_token" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_core_jwt_refresh_token"
|
||||
ADD CONSTRAINT "serverpod_auth_core_jwt_refresh_token_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_core_profile" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_core_profile"
|
||||
ADD CONSTRAINT "serverpod_auth_core_profile_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
ALTER TABLE ONLY "serverpod_auth_core_profile"
|
||||
ADD CONSTRAINT "serverpod_auth_core_profile_fk_1"
|
||||
FOREIGN KEY("imageId")
|
||||
REFERENCES "serverpod_auth_core_profile_image"("id")
|
||||
ON DELETE NO ACTION
|
||||
ON UPDATE NO ACTION
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_core_profile_image" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_core_profile_image"
|
||||
ADD CONSTRAINT "serverpod_auth_core_profile_image_fk_0"
|
||||
FOREIGN KEY("userProfileId")
|
||||
REFERENCES "serverpod_auth_core_profile"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- Foreign relations for "serverpod_auth_core_session" table
|
||||
--
|
||||
ALTER TABLE ONLY "serverpod_auth_core_session"
|
||||
ADD CONSTRAINT "serverpod_auth_core_session_fk_0"
|
||||
FOREIGN KEY("authUserId")
|
||||
REFERENCES "serverpod_auth_core_user"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR halkyon_base
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('halkyon_base', '20261020185619505-recovery-proof-key', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20261020185619505-recovery-proof-key', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod', '20260824182259319', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260824182259319', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod_auth_idp
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod_auth_idp', '20260924105404509', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260924105404509', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod_auth_core
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod_auth_core', '20260924105232991', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260924105232991', "timestamp" = now();
|
||||
|
||||
|
||||
COMMIT;
|
||||
|
|
@ -0,0 +1,945 @@
|
|||
{
|
||||
"__className__": "serverpod.DatabaseDefinition",
|
||||
"schemaVersion": 2,
|
||||
"moduleName": "halkyon_base",
|
||||
"tables": [
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "account_deletion_notice",
|
||||
"dartName": "AccountDeletionNotice",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"columnDefault": "serial",
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "account",
|
||||
"fieldName": "account",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "member",
|
||||
"fieldName": "member",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "createdAt",
|
||||
"fieldName": "createdAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "attempts",
|
||||
"fieldName": "attempts",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"dartType": "int"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "lastAttemptAt",
|
||||
"fieldName": "lastAttemptAt",
|
||||
"columnType": 4,
|
||||
"isNullable": true,
|
||||
"dartType": "DateTime?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "account_deletion_notice_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "account"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "member"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": true,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "account_password",
|
||||
"dartName": "AccountPassword",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"columnDefault": "serial",
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authUserId",
|
||||
"fieldName": "authUserId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "salt",
|
||||
"fieldName": "salt",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "kdfAlgorithm",
|
||||
"fieldName": "kdfAlgorithm",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "kdfOpsLimit",
|
||||
"fieldName": "kdfOpsLimit",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"dartType": "int"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "kdfMemLimit",
|
||||
"fieldName": "kdfMemLimit",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"dartType": "int"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "verifier",
|
||||
"fieldName": "verifier",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "encryptionPublicKey",
|
||||
"fieldName": "encryptionPublicKey",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "signingPublicKey",
|
||||
"fieldName": "signingPublicKey",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "passwordKeysNonce",
|
||||
"fieldName": "passwordKeysNonce",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "passwordKeysCipherText",
|
||||
"fieldName": "passwordKeysCipherText",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "recoveryKeysNonce",
|
||||
"fieldName": "recoveryKeysNonce",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "recoveryKeysCipherText",
|
||||
"fieldName": "recoveryKeysCipherText",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "recoveryProofKey",
|
||||
"fieldName": "recoveryProofKey",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "createdAt",
|
||||
"fieldName": "createdAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "updatedAt",
|
||||
"fieldName": "updatedAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "account_password_person_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "authUserId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": true,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "account_reset",
|
||||
"dartName": "AccountReset",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"columnDefault": "serial",
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authUserId",
|
||||
"fieldName": "authUserId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "kind",
|
||||
"fieldName": "kind",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "protocol:AccountResetKind"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "takesEffectAt",
|
||||
"fieldName": "takesEffectAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "createdAt",
|
||||
"fieldName": "createdAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "account_reset_person_kind_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "authUserId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "kind"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": true,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "email_code_request",
|
||||
"dartName": "EmailCodeRequest",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "addressHash",
|
||||
"fieldName": "addressHash",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "challengeId",
|
||||
"fieldName": "challengeId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "switchChallengeId",
|
||||
"fieldName": "switchChallengeId",
|
||||
"columnType": 7,
|
||||
"isNullable": true,
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "createdAt",
|
||||
"fieldName": "createdAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "email_code_request_fk_0",
|
||||
"columns": [
|
||||
"challengeId"
|
||||
],
|
||||
"referenceTable": "serverpod_auth_idp_secret_challenge",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "email_code_request_fk_1",
|
||||
"columns": [
|
||||
"switchChallengeId"
|
||||
],
|
||||
"referenceTable": "serverpod_auth_idp_secret_challenge",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "email_code_request_address_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "addressHash"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "email_identity",
|
||||
"dartName": "EmailIdentity",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"columnDefault": "serial",
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authUserId",
|
||||
"fieldName": "authUserId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "addressHash",
|
||||
"fieldName": "addressHash",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "createdAt",
|
||||
"fieldName": "createdAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "email_identity_person_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "authUserId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": true,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "email_identity_address_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "addressHash"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": true,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "public_profile",
|
||||
"dartName": "PublicProfile",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"columnDefault": "serial",
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authUserId",
|
||||
"fieldName": "authUserId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "handle",
|
||||
"fieldName": "handle",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "handleKey",
|
||||
"fieldName": "handleKey",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "displayName",
|
||||
"fieldName": "displayName",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "bio",
|
||||
"fieldName": "bio",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "links",
|
||||
"fieldName": "links",
|
||||
"columnType": 8,
|
||||
"isNullable": false,
|
||||
"dartType": "List<String>"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "verifiedLinks",
|
||||
"fieldName": "verifiedLinks",
|
||||
"columnType": 8,
|
||||
"isNullable": true,
|
||||
"dartType": "List<String>?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "avatarHash",
|
||||
"fieldName": "avatarHash",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "firstPublishedAt",
|
||||
"fieldName": "firstPublishedAt",
|
||||
"columnType": 4,
|
||||
"isNullable": true,
|
||||
"dartType": "DateTime?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "updatedAt",
|
||||
"fieldName": "updatedAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "public_profile_person_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "authUserId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": true,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "public_profile_handle_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "handleKey"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": true,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "recovery_code",
|
||||
"dartName": "RecoveryCode",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"columnDefault": "serial",
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authUserId",
|
||||
"fieldName": "authUserId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "hash",
|
||||
"fieldName": "hash",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "recovery_code_person_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "authUserId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "sessions_ended_notice",
|
||||
"dartName": "SessionsEndedNotice",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"columnDefault": "serial",
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "account",
|
||||
"fieldName": "account",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "member",
|
||||
"fieldName": "member",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "endedBefore",
|
||||
"fieldName": "endedBefore",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keepSession",
|
||||
"fieldName": "keepSession",
|
||||
"columnType": 7,
|
||||
"isNullable": true,
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "attempts",
|
||||
"fieldName": "attempts",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"dartType": "int"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "lastAttemptAt",
|
||||
"fieldName": "lastAttemptAt",
|
||||
"columnType": 4,
|
||||
"isNullable": true,
|
||||
"dartType": "DateTime?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "sessions_ended_notice_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "account"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "member"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": true,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "sign_in_challenge",
|
||||
"dartName": "SignInChallenge",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authUserId",
|
||||
"fieldName": "authUserId",
|
||||
"columnType": 7,
|
||||
"isNullable": true,
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "purpose",
|
||||
"fieldName": "purpose",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "protocol:SignInChallengePurpose"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "addressHash",
|
||||
"fieldName": "addressHash",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "secret",
|
||||
"fieldName": "secret",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "serverValue",
|
||||
"fieldName": "serverValue",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "createdAt",
|
||||
"fieldName": "createdAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "sign_in_challenge_created_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "createdAt"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "totp_authenticator",
|
||||
"dartName": "TotpAuthenticator",
|
||||
"module": "halkyon_base",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "authUserId",
|
||||
"fieldName": "authUserId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "name",
|
||||
"fieldName": "name",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "secret",
|
||||
"fieldName": "secret",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "confirmedAt",
|
||||
"fieldName": "confirmedAt",
|
||||
"columnType": 4,
|
||||
"isNullable": true,
|
||||
"dartType": "DateTime?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "lastStep",
|
||||
"fieldName": "lastStep",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "createdAt",
|
||||
"fieldName": "createdAt",
|
||||
"columnType": 4,
|
||||
"isNullable": false,
|
||||
"dartType": "DateTime"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "totp_authenticator_person_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "authUserId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
],
|
||||
"installedModules": [
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod",
|
||||
"version": "20260824182259319"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod_auth_idp",
|
||||
"version": "20260924105404509"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationVersion",
|
||||
"module": "serverpod_auth_core",
|
||||
"version": "20260924105232991"
|
||||
}
|
||||
],
|
||||
"migrationApiVersion": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"__className__": "serverpod.DatabaseMigration",
|
||||
"actions": [
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "account_password",
|
||||
"schema": "public",
|
||||
"addColumns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "recoveryProofKey",
|
||||
"fieldName": "recoveryProofKey",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [],
|
||||
"deleteForeignKeys": [],
|
||||
"warnings": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"warnings": [],
|
||||
"migrationApiVersion": 1
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue