feat(base,app,keys): keys of shared spaces, sealed to each member
Step 3 of ADR 0044 (D3, D18, D19). A shared space's data keys are sealed on a device to each member's public keys; the instance keeps what devices sealed and never opens it. - halkyon_keys: the safety code of two accounts' public keys (`safetyCode`), 60 digits, the same on both sides. - halkyon_base: the register hands out accounts' public keys (`public-keys-of`, local and remote); the table `shared_space_key` and the endpoint `sharedSpaceKeys` (memberKeys, keysNeeded, seal, mine). Only the Owner and Editors seal, the first key sealed to a member stays, and a member's keys go with their membership. Every host's migration creates the table. - halkyon_app: `ContentCipher` seals a key to another account and opens one sealed by another; `SharedKeys` shares a space's key with its members, seals keys to newcomers and opens the person's own, holding each member to the public keys `KeyPins` pinned on first sight (`key_pins.json` in the person's folder), a change refused until trusted anew. Learn takes them into its `Privacy` in step 4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
d08d8ad545
commit
b72cea1fe0
31 changed files with 105813 additions and 5 deletions
|
|
@ -30,6 +30,7 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
| `profile` | `mine`, `save`, `checkHandle` (with every answer about a new handle, at most 30 an hour per person and 60 per network), `checkLinks` (at most 10 an hour), `setAvatar`, `removeAvatar`, `remove` (what it shows goes, the handle stays, private again: Halkyon Learn's ADR 0020, D27): the caller's own public profile | `identified` |
|
||||
| `profiles` | `byHandle`: anyone's public profile once they went public, whatever the handle's case | signed in |
|
||||
| `sharedSpace` | spaces people share (Halkyon's ADR 0044, step 2), on the host's sync engine through its `SpaceGrants` (refused, `unavailable`, without one): `create(kind)`, the caller its Owner; `mine`; `members(space)`, by membership ID and public profile, never by account, those invited and waiting last; `invite(space, handle, role)`, the Owner's (or any member's where the space lets members invite), a member at once where the person has an account on this service, else invited until they first sign in there (D13, D17), counted as an answer about a handle; `setRole`, `remove`, `handOn`, `delete`, the Owner's; `leave`, the Owner naming who takes the space on, its only member deleting it instead. A space the caller isn't in can't be told from one that doesn't exist (`SharedSpaceNotFoundException`); Owner and Editor write in the engine, a Viewer reads | `identified` |
|
||||
| `sharedSpaceKeys` | a shared space's data keys, each sealed on a device to one member (Halkyon's ADR 0044, D3, step 3), which the instance keeps and never opens: `memberKeys(space)`, the public keys of its members who have keys, by account, as the register hands them out, for a device to seal to and pin (D18); `keysNeeded(space)`, the keys the caller holds that members lack, for the Owner and Editors, none for a Viewer; `seal(space, keys)`, refused for a Viewer (`notAllowed`) and for anyone outside the space, the first key sealed to a member staying; `mine(space)`, those sealed to the caller, each naming its sealer. A member's keys go with their membership. A space the caller isn't in can't be told from one that doesn't exist | `identified` |
|
||||
|
||||
- Email keeps only HMAC-SHA256 of the address, keyed with `emailSignInPepper` (`email_identity`), and its
|
||||
codes as Argon2id hashes (`email_code_request`). Codes are eight Crockford characters,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ import 'package:halkyon_base_server/src/generated/password/wrapped_keys.dart'
|
|||
as _iji0wmu8;
|
||||
import 'package:halkyon_base_server/src/generated/shared_space/shared_space_role.dart'
|
||||
as _i4jk9by9;
|
||||
import 'package:halkyon_base_server/src/generated/shared_space/shared_space_sealed_key.dart'
|
||||
as _ik1elru1;
|
||||
import 'package:halkyon_base_server/src/generated/simplelogin/simplelogin_return.dart'
|
||||
as _igmh0yku;
|
||||
import 'package:halkyon_oidc_server/halkyon_oidc_server.dart' as _ipoqfiqr;
|
||||
|
|
@ -60,6 +62,7 @@ import '../password/password_endpoint.dart' as _ig7fyg1b;
|
|||
import '../password/password_sign_in_endpoint.dart' as _ik13m562;
|
||||
import '../profile/profile_endpoint.dart' as _i6ky944g;
|
||||
import '../shared_space/shared_space_endpoint.dart' as _i858tn7r;
|
||||
import '../shared_space/shared_space_keys_endpoint.dart' as _izzpx5a5;
|
||||
import '../simplelogin/simplelogin_endpoint.dart' as _ia8c9alv;
|
||||
|
||||
class Endpoints extends _is.EndpointDispatch {
|
||||
|
|
@ -162,6 +165,12 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
'sharedSpace',
|
||||
'halkyon_base',
|
||||
),
|
||||
'sharedSpaceKeys': _izzpx5a5.SharedSpaceKeysEndpoint()
|
||||
..initialize(
|
||||
server,
|
||||
'sharedSpaceKeys',
|
||||
'halkyon_base',
|
||||
),
|
||||
'simpleLogin': _ia8c9alv.SimpleLoginEndpoint()
|
||||
..initialize(
|
||||
server,
|
||||
|
|
@ -2134,6 +2143,102 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
),
|
||||
},
|
||||
);
|
||||
connectors['sharedSpaceKeys'] = _is.EndpointConnector(
|
||||
name: 'sharedSpaceKeys',
|
||||
endpoint: endpoints['sharedSpaceKeys']!,
|
||||
methodConnectors: {
|
||||
'memberKeys': _is.MethodConnector(
|
||||
name: 'memberKeys',
|
||||
params: {
|
||||
'space': _is.ParameterDescription(
|
||||
name: 'space',
|
||||
type: _is.getType<_is.UuidValue>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['sharedSpaceKeys']
|
||||
as _izzpx5a5.SharedSpaceKeysEndpoint)
|
||||
.memberKeys(
|
||||
session,
|
||||
params['space'],
|
||||
),
|
||||
),
|
||||
'keysNeeded': _is.MethodConnector(
|
||||
name: 'keysNeeded',
|
||||
params: {
|
||||
'space': _is.ParameterDescription(
|
||||
name: 'space',
|
||||
type: _is.getType<_is.UuidValue>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['sharedSpaceKeys']
|
||||
as _izzpx5a5.SharedSpaceKeysEndpoint)
|
||||
.keysNeeded(
|
||||
session,
|
||||
params['space'],
|
||||
),
|
||||
),
|
||||
'seal': _is.MethodConnector(
|
||||
name: 'seal',
|
||||
params: {
|
||||
'space': _is.ParameterDescription(
|
||||
name: 'space',
|
||||
type: _is.getType<_is.UuidValue>(),
|
||||
nullable: false,
|
||||
),
|
||||
'keys': _is.ParameterDescription(
|
||||
name: 'keys',
|
||||
type: _is.getType<List<_ik1elru1.SharedSpaceSealedKey>>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['sharedSpaceKeys']
|
||||
as _izzpx5a5.SharedSpaceKeysEndpoint)
|
||||
.seal(
|
||||
session,
|
||||
params['space'],
|
||||
params['keys'],
|
||||
),
|
||||
),
|
||||
'mine': _is.MethodConnector(
|
||||
name: 'mine',
|
||||
params: {
|
||||
'space': _is.ParameterDescription(
|
||||
name: 'space',
|
||||
type: _is.getType<_is.UuidValue>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['sharedSpaceKeys']
|
||||
as _izzpx5a5.SharedSpaceKeysEndpoint)
|
||||
.mine(
|
||||
session,
|
||||
params['space'],
|
||||
),
|
||||
),
|
||||
},
|
||||
);
|
||||
connectors['simpleLogin'] = _is.EndpointConnector(
|
||||
name: 'simpleLogin',
|
||||
endpoint: endpoints['simpleLogin']!,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// An account's public keys as the register keeps them (ADR 0017): X25519's,
|
||||
/// which data keys are sealed to, and Ed25519's, which its signatures check
|
||||
/// against. What others seal shared keys to and check them by (Halkyon's ADR
|
||||
/// 0044, D3 and D18).
|
||||
abstract class AccountPublicKeys
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
AccountPublicKeys._({
|
||||
required this.encryption,
|
||||
required this.signing,
|
||||
});
|
||||
|
||||
factory AccountPublicKeys({
|
||||
required _idt.ByteData encryption,
|
||||
required _idt.ByteData signing,
|
||||
}) = _AccountPublicKeysImpl;
|
||||
|
||||
factory AccountPublicKeys.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return AccountPublicKeys(
|
||||
encryption: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['encryption'],
|
||||
),
|
||||
signing: _is.ByteDataJsonExtension.fromJson(jsonSerialization['signing']),
|
||||
);
|
||||
}
|
||||
|
||||
_idt.ByteData encryption;
|
||||
|
||||
_idt.ByteData signing;
|
||||
|
||||
/// Returns a shallow copy of this [AccountPublicKeys]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
AccountPublicKeys copyWith({
|
||||
_idt.ByteData? encryption,
|
||||
_idt.ByteData? signing,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.AccountPublicKeys',
|
||||
'encryption': encryption.toJson(),
|
||||
'signing': signing.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _AccountPublicKeysImpl extends AccountPublicKeys {
|
||||
_AccountPublicKeysImpl({
|
||||
required _idt.ByteData encryption,
|
||||
required _idt.ByteData signing,
|
||||
}) : super._(
|
||||
encryption: encryption,
|
||||
signing: signing,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [AccountPublicKeys]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
AccountPublicKeys copyWith({
|
||||
_idt.ByteData? encryption,
|
||||
_idt.ByteData? signing,
|
||||
}) {
|
||||
return AccountPublicKeys(
|
||||
encryption: encryption ?? this.encryption.clone(),
|
||||
signing: signing ?? this.signing.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,8 +21,14 @@ import 'package:halkyon_base_server/src/generated/password/authenticator.dart'
|
|||
as _ip34xal6;
|
||||
import 'package:halkyon_base_server/src/generated/shared_space/shared_space_info.dart'
|
||||
as _i582118y;
|
||||
import 'package:halkyon_base_server/src/generated/shared_space/shared_space_key_need.dart'
|
||||
as _ije0apa9;
|
||||
import 'package:halkyon_base_server/src/generated/shared_space/shared_space_member_info.dart'
|
||||
as _izxzwa6x;
|
||||
import 'package:halkyon_base_server/src/generated/shared_space/shared_space_member_keys.dart'
|
||||
as _ia0j1un6;
|
||||
import 'package:halkyon_base_server/src/generated/shared_space/shared_space_sealed_key.dart'
|
||||
as _ik1elru1;
|
||||
import 'package:halkyon_oidc_server/halkyon_oidc_server.dart' as _ipoqfiqr;
|
||||
import 'package:serverpod/protocol.dart' as _isp;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
|
@ -71,6 +77,7 @@ import 'files/file_limit_exceeded_exception.dart' as _iaz1i85g;
|
|||
import 'files/file_usage.dart' as _i45yjkiy;
|
||||
import 'files/stored_file_not_found_exception.dart' as _i8o9pcsj;
|
||||
import 'instance/instance_basics.dart' as _i8jixqx6;
|
||||
import 'keys/account_public_keys.dart' as _iiomvou1;
|
||||
import 'keys/device_keys.dart' as _iz50cwqw;
|
||||
import 'keys/keys_challenge.dart' as _itebbsfc;
|
||||
import 'keys/keys_owner_proof.dart' as _iis161ma;
|
||||
|
|
@ -116,12 +123,16 @@ import 'register/sessions_ended_notice.dart' as _io5c3tod;
|
|||
import 'shared_space/shared_space.dart' as _iampeqql;
|
||||
import 'shared_space/shared_space_info.dart' as _i1rvc02f;
|
||||
import 'shared_space/shared_space_invitation.dart' as _i7h4we7t;
|
||||
import 'shared_space/shared_space_key.dart' as _i9bjihm2;
|
||||
import 'shared_space/shared_space_key_need.dart' as _imxqlbwp;
|
||||
import 'shared_space/shared_space_member.dart' as _iw7ixstr;
|
||||
import 'shared_space/shared_space_member_info.dart' as _iz8z2oju;
|
||||
import 'shared_space/shared_space_member_keys.dart' as _iqkqjhh4;
|
||||
import 'shared_space/shared_space_not_found_exception.dart' as _ixm56fil;
|
||||
import 'shared_space/shared_space_refusal.dart' as _irg3s7i7;
|
||||
import 'shared_space/shared_space_refused_exception.dart' as _iooym5cy;
|
||||
import 'shared_space/shared_space_role.dart' as _i2fude1g;
|
||||
import 'shared_space/shared_space_sealed_key.dart' as _ifx6y5sl;
|
||||
import 'simplelogin/simplelogin_completed.dart' as _i51ry5s2;
|
||||
import 'simplelogin/simplelogin_outcome.dart' as _i5ar1hzl;
|
||||
import 'simplelogin/simplelogin_proof.dart' as _iauqtzog;
|
||||
|
|
@ -167,6 +178,7 @@ export 'files/file_limit_exceeded_exception.dart';
|
|||
export 'files/file_usage.dart';
|
||||
export 'files/stored_file_not_found_exception.dart';
|
||||
export 'instance/instance_basics.dart';
|
||||
export 'keys/account_public_keys.dart';
|
||||
export 'keys/device_keys.dart';
|
||||
export 'keys/keys_challenge.dart';
|
||||
export 'keys/keys_owner_proof.dart';
|
||||
|
|
@ -212,12 +224,16 @@ export 'register/sessions_ended_notice.dart';
|
|||
export 'shared_space/shared_space.dart';
|
||||
export 'shared_space/shared_space_info.dart';
|
||||
export 'shared_space/shared_space_invitation.dart';
|
||||
export 'shared_space/shared_space_key.dart';
|
||||
export 'shared_space/shared_space_key_need.dart';
|
||||
export 'shared_space/shared_space_member.dart';
|
||||
export 'shared_space/shared_space_member_info.dart';
|
||||
export 'shared_space/shared_space_member_keys.dart';
|
||||
export 'shared_space/shared_space_not_found_exception.dart';
|
||||
export 'shared_space/shared_space_refusal.dart';
|
||||
export 'shared_space/shared_space_refused_exception.dart';
|
||||
export 'shared_space/shared_space_role.dart';
|
||||
export 'shared_space/shared_space_sealed_key.dart';
|
||||
export 'simplelogin/simplelogin_completed.dart';
|
||||
export 'simplelogin/simplelogin_outcome.dart';
|
||||
export 'simplelogin/simplelogin_proof.dart';
|
||||
|
|
@ -1402,6 +1418,106 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'shared_space_key',
|
||||
dartName: 'SharedSpaceKey',
|
||||
schema: 'public',
|
||||
module: 'halkyon_base',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'sharedSpaceId',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'keyId',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'recipientId',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'sealedKey',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: false,
|
||||
dartType: 'dart:typed_data:ByteData',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'sealedBy',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'createdAt',
|
||||
columnType: _isp.ColumnType.timestampWithoutTimeZone,
|
||||
isNullable: false,
|
||||
dartType: 'DateTime',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'shared_space_key_fk_0',
|
||||
columns: ['sharedSpaceId'],
|
||||
referenceTable: 'shared_space',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'shared_space_key_unique_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'sharedSpaceId',
|
||||
),
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'keyId',
|
||||
),
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'recipientId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: true,
|
||||
isPrimary: false,
|
||||
),
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'shared_space_key_recipient_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'recipientId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'shared_space_member',
|
||||
dartName: 'SharedSpaceMember',
|
||||
|
|
@ -1905,6 +2021,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _i8jixqx6.InstanceBasics) {
|
||||
return _i8jixqx6.InstanceBasics.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iiomvou1.AccountPublicKeys) {
|
||||
return _iiomvou1.AccountPublicKeys.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iz50cwqw.DeviceKeys) {
|
||||
return _iz50cwqw.DeviceKeys.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -2040,12 +2159,21 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _i7h4we7t.SharedSpaceInvitation) {
|
||||
return _i7h4we7t.SharedSpaceInvitation.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i9bjihm2.SharedSpaceKey) {
|
||||
return _i9bjihm2.SharedSpaceKey.fromJson(data) as T;
|
||||
}
|
||||
if (t == _imxqlbwp.SharedSpaceKeyNeed) {
|
||||
return _imxqlbwp.SharedSpaceKeyNeed.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iw7ixstr.SharedSpaceMember) {
|
||||
return _iw7ixstr.SharedSpaceMember.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iz8z2oju.SharedSpaceMemberInfo) {
|
||||
return _iz8z2oju.SharedSpaceMemberInfo.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iqkqjhh4.SharedSpaceMemberKeys) {
|
||||
return _iqkqjhh4.SharedSpaceMemberKeys.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ixm56fil.SharedSpaceNotFoundException) {
|
||||
return _ixm56fil.SharedSpaceNotFoundException.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -2058,6 +2186,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _i2fude1g.SharedSpaceRole) {
|
||||
return _i2fude1g.SharedSpaceRole.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ifx6y5sl.SharedSpaceSealedKey) {
|
||||
return _ifx6y5sl.SharedSpaceSealedKey.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i51ry5s2.SimpleLoginCompleted) {
|
||||
return _i51ry5s2.SimpleLoginCompleted.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -2242,6 +2373,10 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return (data != null ? _i8jixqx6.InstanceBasics.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_iiomvou1.AccountPublicKeys?>()) {
|
||||
return (data != null ? _iiomvou1.AccountPublicKeys.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_iz50cwqw.DeviceKeys?>()) {
|
||||
return (data != null ? _iz50cwqw.DeviceKeys.fromJson(data) : null) as T;
|
||||
}
|
||||
|
|
@ -2432,6 +2567,14 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
: null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_i9bjihm2.SharedSpaceKey?>()) {
|
||||
return (data != null ? _i9bjihm2.SharedSpaceKey.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_imxqlbwp.SharedSpaceKeyNeed?>()) {
|
||||
return (data != null ? _imxqlbwp.SharedSpaceKeyNeed.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_iw7ixstr.SharedSpaceMember?>()) {
|
||||
return (data != null ? _iw7ixstr.SharedSpaceMember.fromJson(data) : null)
|
||||
as T;
|
||||
|
|
@ -2442,6 +2585,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
: null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_iqkqjhh4.SharedSpaceMemberKeys?>()) {
|
||||
return (data != null
|
||||
? _iqkqjhh4.SharedSpaceMemberKeys.fromJson(data)
|
||||
: null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_ixm56fil.SharedSpaceNotFoundException?>()) {
|
||||
return (data != null
|
||||
? _ixm56fil.SharedSpaceNotFoundException.fromJson(data)
|
||||
|
|
@ -2462,6 +2611,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return (data != null ? _i2fude1g.SharedSpaceRole.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_ifx6y5sl.SharedSpaceSealedKey?>()) {
|
||||
return (data != null
|
||||
? _ifx6y5sl.SharedSpaceSealedKey.fromJson(data)
|
||||
: null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_i51ry5s2.SimpleLoginCompleted?>()) {
|
||||
return (data != null
|
||||
? _i51ry5s2.SimpleLoginCompleted.fromJson(data)
|
||||
|
|
@ -2577,6 +2732,24 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
.toList()
|
||||
as T;
|
||||
}
|
||||
if (t == List<_ia0j1un6.SharedSpaceMemberKeys>) {
|
||||
return (data as List)
|
||||
.map((e) => deserialize<_ia0j1un6.SharedSpaceMemberKeys>(e))
|
||||
.toList()
|
||||
as T;
|
||||
}
|
||||
if (t == List<_ije0apa9.SharedSpaceKeyNeed>) {
|
||||
return (data as List)
|
||||
.map((e) => deserialize<_ije0apa9.SharedSpaceKeyNeed>(e))
|
||||
.toList()
|
||||
as T;
|
||||
}
|
||||
if (t == List<_ik1elru1.SharedSpaceSealedKey>) {
|
||||
return (data as List)
|
||||
.map((e) => deserialize<_ik1elru1.SharedSpaceSealedKey>(e))
|
||||
.toList()
|
||||
as T;
|
||||
}
|
||||
try {
|
||||
return _ipoqfiqr.Protocol().deserialize<T>(data, t);
|
||||
} on _is.DeserializationTypeNotFoundException catch (_) {}
|
||||
|
|
@ -2635,6 +2808,7 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
_i45yjkiy.FileUsage => 'FileUsage',
|
||||
_i8o9pcsj.StoredFileNotFoundException => 'StoredFileNotFoundException',
|
||||
_i8jixqx6.InstanceBasics => 'InstanceBasics',
|
||||
_iiomvou1.AccountPublicKeys => 'AccountPublicKeys',
|
||||
_iz50cwqw.DeviceKeys => 'DeviceKeys',
|
||||
_itebbsfc.KeysChallenge => 'KeysChallenge',
|
||||
_iis161ma.KeysOwnerProof => 'KeysOwnerProof',
|
||||
|
|
@ -2680,12 +2854,16 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
_iampeqql.SharedSpace => 'SharedSpace',
|
||||
_i1rvc02f.SharedSpaceInfo => 'SharedSpaceInfo',
|
||||
_i7h4we7t.SharedSpaceInvitation => 'SharedSpaceInvitation',
|
||||
_i9bjihm2.SharedSpaceKey => 'SharedSpaceKey',
|
||||
_imxqlbwp.SharedSpaceKeyNeed => 'SharedSpaceKeyNeed',
|
||||
_iw7ixstr.SharedSpaceMember => 'SharedSpaceMember',
|
||||
_iz8z2oju.SharedSpaceMemberInfo => 'SharedSpaceMemberInfo',
|
||||
_iqkqjhh4.SharedSpaceMemberKeys => 'SharedSpaceMemberKeys',
|
||||
_ixm56fil.SharedSpaceNotFoundException => 'SharedSpaceNotFoundException',
|
||||
_irg3s7i7.SharedSpaceRefusal => 'SharedSpaceRefusal',
|
||||
_iooym5cy.SharedSpaceRefusedException => 'SharedSpaceRefusedException',
|
||||
_i2fude1g.SharedSpaceRole => 'SharedSpaceRole',
|
||||
_ifx6y5sl.SharedSpaceSealedKey => 'SharedSpaceSealedKey',
|
||||
_i51ry5s2.SimpleLoginCompleted => 'SimpleLoginCompleted',
|
||||
_i5ar1hzl.SimpleLoginOutcome => 'SimpleLoginOutcome',
|
||||
_iauqtzog.SimpleLoginProof => 'SimpleLoginProof',
|
||||
|
|
@ -2789,6 +2967,8 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return 'StoredFileNotFoundException';
|
||||
case _i8jixqx6.InstanceBasics():
|
||||
return 'InstanceBasics';
|
||||
case _iiomvou1.AccountPublicKeys():
|
||||
return 'AccountPublicKeys';
|
||||
case _iz50cwqw.DeviceKeys():
|
||||
return 'DeviceKeys';
|
||||
case _itebbsfc.KeysChallenge():
|
||||
|
|
@ -2879,10 +3059,16 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return 'SharedSpaceInfo';
|
||||
case _i7h4we7t.SharedSpaceInvitation():
|
||||
return 'SharedSpaceInvitation';
|
||||
case _i9bjihm2.SharedSpaceKey():
|
||||
return 'SharedSpaceKey';
|
||||
case _imxqlbwp.SharedSpaceKeyNeed():
|
||||
return 'SharedSpaceKeyNeed';
|
||||
case _iw7ixstr.SharedSpaceMember():
|
||||
return 'SharedSpaceMember';
|
||||
case _iz8z2oju.SharedSpaceMemberInfo():
|
||||
return 'SharedSpaceMemberInfo';
|
||||
case _iqkqjhh4.SharedSpaceMemberKeys():
|
||||
return 'SharedSpaceMemberKeys';
|
||||
case _ixm56fil.SharedSpaceNotFoundException():
|
||||
return 'SharedSpaceNotFoundException';
|
||||
case _irg3s7i7.SharedSpaceRefusal():
|
||||
|
|
@ -2891,6 +3077,8 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return 'SharedSpaceRefusedException';
|
||||
case _i2fude1g.SharedSpaceRole():
|
||||
return 'SharedSpaceRole';
|
||||
case _ifx6y5sl.SharedSpaceSealedKey():
|
||||
return 'SharedSpaceSealedKey';
|
||||
case _i51ry5s2.SimpleLoginCompleted():
|
||||
return 'SimpleLoginCompleted';
|
||||
case _i5ar1hzl.SimpleLoginOutcome():
|
||||
|
|
@ -3036,6 +3224,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName == 'InstanceBasics') {
|
||||
return deserialize<_i8jixqx6.InstanceBasics>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'AccountPublicKeys') {
|
||||
return deserialize<_iiomvou1.AccountPublicKeys>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'DeviceKeys') {
|
||||
return deserialize<_iz50cwqw.DeviceKeys>(data['data']);
|
||||
}
|
||||
|
|
@ -3171,12 +3362,21 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName == 'SharedSpaceInvitation') {
|
||||
return deserialize<_i7h4we7t.SharedSpaceInvitation>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SharedSpaceKey') {
|
||||
return deserialize<_i9bjihm2.SharedSpaceKey>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SharedSpaceKeyNeed') {
|
||||
return deserialize<_imxqlbwp.SharedSpaceKeyNeed>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SharedSpaceMember') {
|
||||
return deserialize<_iw7ixstr.SharedSpaceMember>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SharedSpaceMemberInfo') {
|
||||
return deserialize<_iz8z2oju.SharedSpaceMemberInfo>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SharedSpaceMemberKeys') {
|
||||
return deserialize<_iqkqjhh4.SharedSpaceMemberKeys>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SharedSpaceNotFoundException') {
|
||||
return deserialize<_ixm56fil.SharedSpaceNotFoundException>(data['data']);
|
||||
}
|
||||
|
|
@ -3189,6 +3389,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName == 'SharedSpaceRole') {
|
||||
return deserialize<_i2fude1g.SharedSpaceRole>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SharedSpaceSealedKey') {
|
||||
return deserialize<_ifx6y5sl.SharedSpaceSealedKey>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SimpleLoginCompleted') {
|
||||
return deserialize<_i51ry5s2.SimpleLoginCompleted>(data['data']);
|
||||
}
|
||||
|
|
@ -3334,6 +3537,8 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return _iampeqql.SharedSpace.t;
|
||||
case _i7h4we7t.SharedSpaceInvitation:
|
||||
return _i7h4we7t.SharedSpaceInvitation.t;
|
||||
case _i9bjihm2.SharedSpaceKey:
|
||||
return _i9bjihm2.SharedSpaceKey.t;
|
||||
case _iw7ixstr.SharedSpaceMember:
|
||||
return _iw7ixstr.SharedSpaceMember.t;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,11 @@ sharedSpace:
|
|||
- handOn:
|
||||
- leave:
|
||||
- delete:
|
||||
sharedSpaceKeys:
|
||||
- memberKeys:
|
||||
- keysNeeded:
|
||||
- seal:
|
||||
- mine:
|
||||
simpleLogin:
|
||||
- begin:
|
||||
- complete:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,722 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A data key of a shared space sealed to one member (ADR 0044, D3): by a
|
||||
/// device of a member who writes and holds it, to the member's X25519
|
||||
/// public key, signed by the sealer (halkyon_keys' `sealDataKey`). The
|
||||
/// instance keeps it as it is and never opens it. The first one sealed for
|
||||
/// a member and key stays; the member's go with their membership.
|
||||
abstract class SharedSpaceKey
|
||||
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
|
||||
SharedSpaceKey._({
|
||||
this.id,
|
||||
required this.sharedSpaceId,
|
||||
required this.keyId,
|
||||
required this.recipientId,
|
||||
required this.sealedKey,
|
||||
required this.sealedBy,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory SharedSpaceKey({
|
||||
_is.UuidValue? id,
|
||||
required _is.UuidValue sharedSpaceId,
|
||||
required String keyId,
|
||||
required _is.UuidValue recipientId,
|
||||
required _idt.ByteData sealedKey,
|
||||
required _is.UuidValue sealedBy,
|
||||
required DateTime createdAt,
|
||||
}) = _SharedSpaceKeyImpl;
|
||||
|
||||
factory SharedSpaceKey.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return SharedSpaceKey(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
sharedSpaceId: _is.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['sharedSpaceId'],
|
||||
),
|
||||
keyId: jsonSerialization['keyId'] as String,
|
||||
recipientId: _is.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['recipientId'],
|
||||
),
|
||||
sealedKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['sealedKey'],
|
||||
),
|
||||
sealedBy: _is.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['sealedBy'],
|
||||
),
|
||||
createdAt: _is.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['createdAt'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final t = SharedSpaceKeyTable();
|
||||
|
||||
static const db = SharedSpaceKeyRepository._();
|
||||
|
||||
@override
|
||||
_is.UuidValue? id;
|
||||
|
||||
_is.UuidValue sharedSpaceId;
|
||||
|
||||
/// The key's ID, which the rows it seals name in their `keyId`.
|
||||
String keyId;
|
||||
|
||||
/// Whom it is sealed to: the register's account ID.
|
||||
_is.UuidValue recipientId;
|
||||
|
||||
/// The sealer's signature, then the sealed box.
|
||||
_idt.ByteData sealedKey;
|
||||
|
||||
/// Who sealed it: the register's account ID, whose signing key checks it.
|
||||
_is.UuidValue sealedBy;
|
||||
|
||||
DateTime createdAt;
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [SharedSpaceKey]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
SharedSpaceKey copyWith({
|
||||
_is.UuidValue? id,
|
||||
_is.UuidValue? sharedSpaceId,
|
||||
String? keyId,
|
||||
_is.UuidValue? recipientId,
|
||||
_idt.ByteData? sealedKey,
|
||||
_is.UuidValue? sealedBy,
|
||||
DateTime? createdAt,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.SharedSpaceKey',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
'sharedSpaceId': sharedSpaceId.toJson(),
|
||||
'keyId': keyId,
|
||||
'recipientId': recipientId.toJson(),
|
||||
'sealedKey': sealedKey.toJson(),
|
||||
'sealedBy': sealedBy.toJson(),
|
||||
'createdAt': createdAt.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {};
|
||||
}
|
||||
|
||||
static SharedSpaceKeyInclude include() {
|
||||
return SharedSpaceKeyInclude._();
|
||||
}
|
||||
|
||||
static SharedSpaceKeyIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<SharedSpaceKeyTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SharedSpaceKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<SharedSpaceKeyTable>? orderByList,
|
||||
SharedSpaceKeyInclude? include,
|
||||
}) {
|
||||
return SharedSpaceKeyIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(SharedSpaceKey.t),
|
||||
orderByList: orderByList?.call(SharedSpaceKey.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _SharedSpaceKeyImpl extends SharedSpaceKey {
|
||||
_SharedSpaceKeyImpl({
|
||||
_is.UuidValue? id,
|
||||
required _is.UuidValue sharedSpaceId,
|
||||
required String keyId,
|
||||
required _is.UuidValue recipientId,
|
||||
required _idt.ByteData sealedKey,
|
||||
required _is.UuidValue sealedBy,
|
||||
required DateTime createdAt,
|
||||
}) : super._(
|
||||
id: id,
|
||||
sharedSpaceId: sharedSpaceId,
|
||||
keyId: keyId,
|
||||
recipientId: recipientId,
|
||||
sealedKey: sealedKey,
|
||||
sealedBy: sealedBy,
|
||||
createdAt: createdAt,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [SharedSpaceKey]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
SharedSpaceKey copyWith({
|
||||
Object? id = _Undefined,
|
||||
_is.UuidValue? sharedSpaceId,
|
||||
String? keyId,
|
||||
_is.UuidValue? recipientId,
|
||||
_idt.ByteData? sealedKey,
|
||||
_is.UuidValue? sealedBy,
|
||||
DateTime? createdAt,
|
||||
}) {
|
||||
return SharedSpaceKey(
|
||||
id: id is _is.UuidValue? ? id : this.id,
|
||||
sharedSpaceId: sharedSpaceId ?? this.sharedSpaceId,
|
||||
keyId: keyId ?? this.keyId,
|
||||
recipientId: recipientId ?? this.recipientId,
|
||||
sealedKey: sealedKey ?? this.sealedKey.clone(),
|
||||
sealedBy: sealedBy ?? this.sealedBy,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SharedSpaceKeyUpdateTable extends _is.UpdateTable<SharedSpaceKeyTable> {
|
||||
SharedSpaceKeyUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<_is.UuidValue, _is.UuidValue> sharedSpaceId(
|
||||
_is.UuidValue value,
|
||||
) => _is.ColumnValue(
|
||||
table.sharedSpaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> keyId(String value) => _is.ColumnValue(
|
||||
table.keyId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_is.UuidValue, _is.UuidValue> recipientId(
|
||||
_is.UuidValue value,
|
||||
) => _is.ColumnValue(
|
||||
table.recipientId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> sealedKey(
|
||||
_idt.ByteData value,
|
||||
) => _is.ColumnValue(
|
||||
table.sealedKey,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_is.UuidValue, _is.UuidValue> sealedBy(_is.UuidValue value) =>
|
||||
_is.ColumnValue(
|
||||
table.sealedBy,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<DateTime, DateTime> createdAt(DateTime value) =>
|
||||
_is.ColumnValue(
|
||||
table.createdAt,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class SharedSpaceKeyTable extends _is.Table<_is.UuidValue?> {
|
||||
SharedSpaceKeyTable({super.tableRelation})
|
||||
: super(tableName: 'shared_space_key') {
|
||||
updateTable = SharedSpaceKeyUpdateTable(this);
|
||||
sharedSpaceId = _is.ColumnUuid(
|
||||
'sharedSpaceId',
|
||||
this,
|
||||
);
|
||||
keyId = _is.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
recipientId = _is.ColumnUuid(
|
||||
'recipientId',
|
||||
this,
|
||||
);
|
||||
sealedKey = _is.ColumnByteData(
|
||||
'sealedKey',
|
||||
this,
|
||||
);
|
||||
sealedBy = _is.ColumnUuid(
|
||||
'sealedBy',
|
||||
this,
|
||||
);
|
||||
createdAt = _is.ColumnDateTime(
|
||||
'createdAt',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final SharedSpaceKeyUpdateTable updateTable;
|
||||
|
||||
late final _is.ColumnUuid sharedSpaceId;
|
||||
|
||||
/// The key's ID, which the rows it seals name in their `keyId`.
|
||||
late final _is.ColumnString keyId;
|
||||
|
||||
/// Whom it is sealed to: the register's account ID.
|
||||
late final _is.ColumnUuid recipientId;
|
||||
|
||||
/// The sealer's signature, then the sealed box.
|
||||
late final _is.ColumnByteData sealedKey;
|
||||
|
||||
/// Who sealed it: the register's account ID, whose signing key checks it.
|
||||
late final _is.ColumnUuid sealedBy;
|
||||
|
||||
late final _is.ColumnDateTime createdAt;
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
sharedSpaceId,
|
||||
keyId,
|
||||
recipientId,
|
||||
sealedKey,
|
||||
sealedBy,
|
||||
createdAt,
|
||||
];
|
||||
}
|
||||
|
||||
class SharedSpaceKeyInclude extends _is.IncludeObject {
|
||||
SharedSpaceKeyInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => SharedSpaceKey.t;
|
||||
}
|
||||
|
||||
class SharedSpaceKeyIncludeList extends _is.IncludeList {
|
||||
SharedSpaceKeyIncludeList._({
|
||||
_is.WhereExpressionBuilder<SharedSpaceKeyTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(SharedSpaceKey.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => SharedSpaceKey.t;
|
||||
}
|
||||
|
||||
class SharedSpaceKeyRepository {
|
||||
const SharedSpaceKeyRepository._();
|
||||
|
||||
/// Returns a list of [SharedSpaceKey]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order of the items use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// The maximum number of items can be set by [limit]. If no limit is set,
|
||||
/// all items matching the query will be returned.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which [limit] (or all)
|
||||
/// items are read from the database.
|
||||
///
|
||||
/// ```dart
|
||||
/// var persons = await Persons.db.find(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.firstName,
|
||||
/// limit: 100,
|
||||
/// );
|
||||
/// ```
|
||||
Future<List<SharedSpaceKey>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<SharedSpaceKeyTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SharedSpaceKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<SharedSpaceKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<SharedSpaceKey>(
|
||||
where: where?.call(SharedSpaceKey.t),
|
||||
orderBy: orderBy?.call(SharedSpaceKey.t),
|
||||
orderByList: orderByList?.call(SharedSpaceKey.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [SharedSpaceKey] matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which the next one will be picked.
|
||||
///
|
||||
/// ```dart
|
||||
/// var youngestPerson = await Persons.db.findFirstRow(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.age,
|
||||
/// );
|
||||
/// ```
|
||||
Future<SharedSpaceKey?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<SharedSpaceKeyTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SharedSpaceKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<SharedSpaceKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<SharedSpaceKey>(
|
||||
where: where?.call(SharedSpaceKey.t),
|
||||
orderBy: orderBy?.call(SharedSpaceKey.t),
|
||||
orderByList: orderByList?.call(SharedSpaceKey.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [SharedSpaceKey] by its [id] or null if no such row exists.
|
||||
Future<SharedSpaceKey?> findById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<SharedSpaceKey>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [SharedSpaceKey]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [SharedSpaceKey]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// insert, none of the rows will be inserted.
|
||||
///
|
||||
/// If [ignoreConflicts] is set to `true`, rows that conflict with existing
|
||||
/// rows are silently skipped, and only the successfully inserted rows are
|
||||
/// returned.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the inserted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<SharedSpaceKey>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<SharedSpaceKey> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<SharedSpaceKey>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [SharedSpaceKey] and returns the inserted row.
|
||||
///
|
||||
/// The returned [SharedSpaceKey] will have its `id` field set.
|
||||
Future<SharedSpaceKey> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
SharedSpaceKey row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<SharedSpaceKey>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [SharedSpaceKey]s in the list and returns the resulting rows.
|
||||
///
|
||||
/// If a row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated with the new values. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies to rows matching the
|
||||
/// given expression. Conflicting rows that don't match are skipped and not
|
||||
/// returned, so the resulting list may be shorter than [rows].
|
||||
///
|
||||
/// The returned [SharedSpaceKey]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails,
|
||||
/// none of the rows will be affected.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the resulting rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<SharedSpaceKey>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<SharedSpaceKey> rows, {
|
||||
required _is.ColumnSelections<SharedSpaceKeyTable> conflictColumns,
|
||||
_is.ColumnSelections<SharedSpaceKeyTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<SharedSpaceKeyTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<SharedSpaceKey>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(SharedSpaceKey.t),
|
||||
updateColumns: updateColumns?.call(SharedSpaceKey.t),
|
||||
updateWhere: updateWhere?.call(SharedSpaceKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [SharedSpaceKey] and returns the resulting row.
|
||||
///
|
||||
/// If the row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies when the existing
|
||||
/// row matches the expression. Returns `null` if no row was affected — for
|
||||
/// example when [updateWhere] does not match the conflicting row.
|
||||
///
|
||||
/// The returned [SharedSpaceKey] will have its `id` field set.
|
||||
Future<SharedSpaceKey?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
SharedSpaceKey row, {
|
||||
required _is.ColumnSelections<SharedSpaceKeyTable> conflictColumns,
|
||||
_is.ColumnSelections<SharedSpaceKeyTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<SharedSpaceKeyTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<SharedSpaceKey>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(SharedSpaceKey.t),
|
||||
updateColumns: updateColumns?.call(SharedSpaceKey.t),
|
||||
updateWhere: updateWhere?.call(SharedSpaceKey.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [SharedSpaceKey]s in the list and returns the updated rows. If
|
||||
/// [columns] is provided, only those columns will be updated. Defaults to
|
||||
/// all columns.
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// update, none of the rows will be updated.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<SharedSpaceKey>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<SharedSpaceKey> rows, {
|
||||
_is.ColumnSelections<SharedSpaceKeyTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<SharedSpaceKey>(
|
||||
rows,
|
||||
columns: columns?.call(SharedSpaceKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [SharedSpaceKey]. The row needs to have its id set.
|
||||
/// Optionally, a list of [columns] can be provided to only update those
|
||||
/// columns. Defaults to all columns.
|
||||
Future<SharedSpaceKey> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
SharedSpaceKey row, {
|
||||
_is.ColumnSelections<SharedSpaceKeyTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<SharedSpaceKey>(
|
||||
row,
|
||||
columns: columns?.call(SharedSpaceKey.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [SharedSpaceKey] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<SharedSpaceKey?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
required _is.ColumnValueListBuilder<SharedSpaceKeyUpdateTable> columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<SharedSpaceKey>(
|
||||
id,
|
||||
columnValues: columnValues(SharedSpaceKey.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [SharedSpaceKey]s matching the [where] expression with the specified [columnValues].
|
||||
/// Returns the list of updated rows.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<SharedSpaceKey>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<SharedSpaceKeyUpdateTable> columnValues,
|
||||
required _is.WhereExpressionBuilder<SharedSpaceKeyTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SharedSpaceKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<SharedSpaceKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<SharedSpaceKey>(
|
||||
columnValues: columnValues(SharedSpaceKey.t.updateTable),
|
||||
where: where(SharedSpaceKey.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(SharedSpaceKey.t),
|
||||
orderByList: orderByList?.call(SharedSpaceKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [SharedSpaceKey]s in the list and returns the deleted rows.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fail to
|
||||
/// be deleted, none of the rows will be deleted.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<SharedSpaceKey>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<SharedSpaceKey> rows, {
|
||||
_is.OrderByBuilder<SharedSpaceKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<SharedSpaceKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<SharedSpaceKey>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(SharedSpaceKey.t),
|
||||
orderByList: orderByList?.call(SharedSpaceKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [SharedSpaceKey].
|
||||
Future<SharedSpaceKey> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
SharedSpaceKey row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<SharedSpaceKey>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all rows matching the [where] expression.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<SharedSpaceKey>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<SharedSpaceKeyTable> where,
|
||||
_is.OrderByBuilder<SharedSpaceKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<SharedSpaceKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<SharedSpaceKey>(
|
||||
where: where(SharedSpaceKey.t),
|
||||
orderBy: orderBy?.call(SharedSpaceKey.t),
|
||||
orderByList: orderByList?.call(SharedSpaceKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Counts the number of rows matching the [where] expression. If omitted,
|
||||
/// will return the count of all rows in the table.
|
||||
Future<int> count(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<SharedSpaceKeyTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<SharedSpaceKey>(
|
||||
where: where?.call(SharedSpaceKey.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [SharedSpaceKey] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<SharedSpaceKeyTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<SharedSpaceKey>(
|
||||
where: where(SharedSpaceKey.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A key the caller holds that a member of the space lacks: for the
|
||||
/// caller's device to seal to them (ADR 0044, D3).
|
||||
abstract class SharedSpaceKeyNeed
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
SharedSpaceKeyNeed._({
|
||||
required this.keyId,
|
||||
required this.person,
|
||||
});
|
||||
|
||||
factory SharedSpaceKeyNeed({
|
||||
required String keyId,
|
||||
required _is.UuidValue person,
|
||||
}) = _SharedSpaceKeyNeedImpl;
|
||||
|
||||
factory SharedSpaceKeyNeed.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return SharedSpaceKeyNeed(
|
||||
keyId: jsonSerialization['keyId'] as String,
|
||||
person: _is.UuidValueJsonExtension.fromJson(jsonSerialization['person']),
|
||||
);
|
||||
}
|
||||
|
||||
String keyId;
|
||||
|
||||
/// The member who lacks it.
|
||||
_is.UuidValue person;
|
||||
|
||||
/// Returns a shallow copy of this [SharedSpaceKeyNeed]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
SharedSpaceKeyNeed copyWith({
|
||||
String? keyId,
|
||||
_is.UuidValue? person,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.SharedSpaceKeyNeed',
|
||||
'keyId': keyId,
|
||||
'person': person.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.SharedSpaceKeyNeed',
|
||||
'keyId': keyId,
|
||||
'person': person.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _SharedSpaceKeyNeedImpl extends SharedSpaceKeyNeed {
|
||||
_SharedSpaceKeyNeedImpl({
|
||||
required String keyId,
|
||||
required _is.UuidValue person,
|
||||
}) : super._(
|
||||
keyId: keyId,
|
||||
person: person,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [SharedSpaceKeyNeed]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
SharedSpaceKeyNeed copyWith({
|
||||
String? keyId,
|
||||
_is.UuidValue? person,
|
||||
}) {
|
||||
return SharedSpaceKeyNeed(
|
||||
keyId: keyId ?? this.keyId,
|
||||
person: person ?? this.person,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A member's public keys, as the register keeps them, which a device pins
|
||||
/// the first time it sees them and seals keys to (ADR 0044, D3 and D18).
|
||||
/// Named by the member's account ID, as the space's synced rows name their
|
||||
/// authors: a device pins keys per person, across every space it shares
|
||||
/// with them.
|
||||
abstract class SharedSpaceMemberKeys
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
SharedSpaceMemberKeys._({
|
||||
required this.person,
|
||||
this.handle,
|
||||
required this.encryption,
|
||||
required this.signing,
|
||||
});
|
||||
|
||||
factory SharedSpaceMemberKeys({
|
||||
required _is.UuidValue person,
|
||||
String? handle,
|
||||
required _idt.ByteData encryption,
|
||||
required _idt.ByteData signing,
|
||||
}) = _SharedSpaceMemberKeysImpl;
|
||||
|
||||
factory SharedSpaceMemberKeys.fromJson(
|
||||
Map<String, dynamic> jsonSerialization,
|
||||
) {
|
||||
return SharedSpaceMemberKeys(
|
||||
person: _is.UuidValueJsonExtension.fromJson(jsonSerialization['person']),
|
||||
handle: jsonSerialization['handle'] as String?,
|
||||
encryption: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['encryption'],
|
||||
),
|
||||
signing: _is.ByteDataJsonExtension.fromJson(jsonSerialization['signing']),
|
||||
);
|
||||
}
|
||||
|
||||
_is.UuidValue person;
|
||||
|
||||
/// Their handle; null without a public profile.
|
||||
String? handle;
|
||||
|
||||
/// X25519's, which keys are sealed to.
|
||||
_idt.ByteData encryption;
|
||||
|
||||
/// Ed25519's, which their signatures check against.
|
||||
_idt.ByteData signing;
|
||||
|
||||
/// Returns a shallow copy of this [SharedSpaceMemberKeys]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
SharedSpaceMemberKeys copyWith({
|
||||
_is.UuidValue? person,
|
||||
String? handle,
|
||||
_idt.ByteData? encryption,
|
||||
_idt.ByteData? signing,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.SharedSpaceMemberKeys',
|
||||
'person': person.toJson(),
|
||||
if (handle != null) 'handle': handle,
|
||||
'encryption': encryption.toJson(),
|
||||
'signing': signing.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.SharedSpaceMemberKeys',
|
||||
'person': person.toJson(),
|
||||
if (handle != null) 'handle': handle,
|
||||
'encryption': encryption.toJson(),
|
||||
'signing': signing.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _SharedSpaceMemberKeysImpl extends SharedSpaceMemberKeys {
|
||||
_SharedSpaceMemberKeysImpl({
|
||||
required _is.UuidValue person,
|
||||
String? handle,
|
||||
required _idt.ByteData encryption,
|
||||
required _idt.ByteData signing,
|
||||
}) : super._(
|
||||
person: person,
|
||||
handle: handle,
|
||||
encryption: encryption,
|
||||
signing: signing,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [SharedSpaceMemberKeys]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
SharedSpaceMemberKeys copyWith({
|
||||
_is.UuidValue? person,
|
||||
Object? handle = _Undefined,
|
||||
_idt.ByteData? encryption,
|
||||
_idt.ByteData? signing,
|
||||
}) {
|
||||
return SharedSpaceMemberKeys(
|
||||
person: person ?? this.person,
|
||||
handle: handle is String? ? handle : this.handle,
|
||||
encryption: encryption ?? this.encryption.clone(),
|
||||
signing: signing ?? this.signing.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A key a device sealed to a member, as it sends it (ADR 0044, D3), or as
|
||||
/// the member gets it back: [person] is whom it is sealed to on the way
|
||||
/// in, and who sealed it on the way out.
|
||||
abstract class SharedSpaceSealedKey
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
SharedSpaceSealedKey._({
|
||||
required this.keyId,
|
||||
required this.person,
|
||||
required this.sealedKey,
|
||||
});
|
||||
|
||||
factory SharedSpaceSealedKey({
|
||||
required String keyId,
|
||||
required _is.UuidValue person,
|
||||
required _idt.ByteData sealedKey,
|
||||
}) = _SharedSpaceSealedKeyImpl;
|
||||
|
||||
factory SharedSpaceSealedKey.fromJson(
|
||||
Map<String, dynamic> jsonSerialization,
|
||||
) {
|
||||
return SharedSpaceSealedKey(
|
||||
keyId: jsonSerialization['keyId'] as String,
|
||||
person: _is.UuidValueJsonExtension.fromJson(jsonSerialization['person']),
|
||||
sealedKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['sealedKey'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String keyId;
|
||||
|
||||
_is.UuidValue person;
|
||||
|
||||
/// The sealer's signature, then the sealed box.
|
||||
_idt.ByteData sealedKey;
|
||||
|
||||
/// Returns a shallow copy of this [SharedSpaceSealedKey]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
SharedSpaceSealedKey copyWith({
|
||||
String? keyId,
|
||||
_is.UuidValue? person,
|
||||
_idt.ByteData? sealedKey,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.SharedSpaceSealedKey',
|
||||
'keyId': keyId,
|
||||
'person': person.toJson(),
|
||||
'sealedKey': sealedKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.SharedSpaceSealedKey',
|
||||
'keyId': keyId,
|
||||
'person': person.toJson(),
|
||||
'sealedKey': sealedKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _SharedSpaceSealedKeyImpl extends SharedSpaceSealedKey {
|
||||
_SharedSpaceSealedKeyImpl({
|
||||
required String keyId,
|
||||
required _is.UuidValue person,
|
||||
required _idt.ByteData sealedKey,
|
||||
}) : super._(
|
||||
keyId: keyId,
|
||||
person: person,
|
||||
sealedKey: sealedKey,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [SharedSpaceSealedKey]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
SharedSpaceSealedKey copyWith({
|
||||
String? keyId,
|
||||
_is.UuidValue? person,
|
||||
_idt.ByteData? sealedKey,
|
||||
}) {
|
||||
return SharedSpaceSealedKey(
|
||||
keyId: keyId ?? this.keyId,
|
||||
person: person ?? this.person,
|
||||
sealedKey: sealedKey ?? this.sealedKey.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
### An account's public keys as the register keeps them (ADR 0017): X25519's,
|
||||
### which data keys are sealed to, and Ed25519's, which its signatures check
|
||||
### against. What others seal shared keys to and check them by (Halkyon's ADR
|
||||
### 0044, D3 and D18).
|
||||
class: AccountPublicKeys
|
||||
serverOnly: true
|
||||
fields:
|
||||
encryption: ByteData
|
||||
signing: ByteData
|
||||
|
|
@ -53,6 +53,14 @@ abstract interface class AccountRegister {
|
|||
Set<UuidValue> people,
|
||||
);
|
||||
|
||||
/// The public keys of those of [people] who have keys (ADR 0017), by
|
||||
/// person: what a device seals a shared key to and checks its sealer by
|
||||
/// (Halkyon's ADR 0044, D3), pinning them (D18).
|
||||
Future<Map<UuidValue, AccountPublicKeys>> publicKeysOf(
|
||||
Session session,
|
||||
Set<UuidValue> people,
|
||||
);
|
||||
|
||||
/// The public profile whose handle is [handle], whatever its case; null for
|
||||
/// none.
|
||||
Future<Profile?> profileByHandle(Session session, String handle);
|
||||
|
|
|
|||
|
|
@ -109,6 +109,22 @@ Future<Map<String, Object?>?> _answer(
|
|||
key.uuid: value.toJson(),
|
||||
},
|
||||
};
|
||||
case RegisterOperation.publicKeysOf:
|
||||
final keys = await register.publicKeysOf(
|
||||
session,
|
||||
arguments.ids('people'),
|
||||
);
|
||||
return {
|
||||
'keys': {
|
||||
for (final MapEntry(:key, :value) in keys.entries)
|
||||
key.uuid: {
|
||||
'encryption': base64Encode(
|
||||
Uint8List.sublistView(value.encryption),
|
||||
),
|
||||
'signing': base64Encode(Uint8List.sublistView(value.signing)),
|
||||
},
|
||||
},
|
||||
};
|
||||
case RegisterOperation.profileByHandle:
|
||||
final profile = await register.profileByHandle(
|
||||
session,
|
||||
|
|
|
|||
|
|
@ -65,6 +65,28 @@ final class LocalRegister implements AccountRegister {
|
|||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<UuidValue, AccountPublicKeys>> publicKeysOf(
|
||||
Session session,
|
||||
Set<UuidValue> people,
|
||||
) async {
|
||||
if (people.isEmpty) {
|
||||
return const {};
|
||||
}
|
||||
// SELECT … FROM "account_password" WHERE "authUserId" = ANY($1)
|
||||
final rows = await AccountPassword.db.find(
|
||||
session,
|
||||
where: (t) => t.authUserId.inSet(people),
|
||||
);
|
||||
return {
|
||||
for (final row in rows)
|
||||
row.authUserId: AccountPublicKeys(
|
||||
encryption: row.encryptionPublicKey,
|
||||
signing: row.signingPublicKey,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Profile?> profileByHandle(Session session, String handle) async {
|
||||
if (!isHandle(handle)) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const registerPath = '/halkyon-base/register';
|
|||
abstract final class RegisterOperation {
|
||||
static const profileOf = 'profile-of';
|
||||
static const profilesOf = 'profiles-of';
|
||||
static const publicKeysOf = 'public-keys-of';
|
||||
static const profileByHandle = 'profile-by-handle';
|
||||
static const personWithHandle = 'person-with-handle';
|
||||
static const saveProfile = 'save-profile';
|
||||
|
|
|
|||
|
|
@ -140,6 +140,32 @@ final class RemoteRegister implements AccountRegister {
|
|||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<UuidValue, AccountPublicKeys>> publicKeysOf(
|
||||
Session session,
|
||||
Set<UuidValue> people,
|
||||
) async {
|
||||
if (people.isEmpty) {
|
||||
return const {};
|
||||
}
|
||||
final answer = await _ask(RegisterOperation.publicKeysOf, {
|
||||
'people': [for (final person in people) person.uuid],
|
||||
});
|
||||
ByteData bytes(Object? value) => switch (value) {
|
||||
final String text => ByteData.sublistView(base64Decode(text)),
|
||||
_ => throw RegisterUnavailableException('publicKeysOf: no keys'),
|
||||
};
|
||||
return {
|
||||
if (answer['keys'] case final Map<String, Object?> keys)
|
||||
for (final MapEntry(:key, :value) in keys.entries)
|
||||
if (value case final Map<String, Object?> pair)
|
||||
UuidValue.withValidation(key): AccountPublicKeys(
|
||||
encryption: bytes(pair['encryption']),
|
||||
signing: bytes(pair['signing']),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Profile?> profileByHandle(Session session, String handle) async =>
|
||||
_profile(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
### A data key of a shared space sealed to one member (ADR 0044, D3): by a
|
||||
### device of a member who writes and holds it, to the member's X25519
|
||||
### public key, signed by the sealer (halkyon_keys' `sealDataKey`). The
|
||||
### instance keeps it as it is and never opens it. The first one sealed for
|
||||
### a member and key stays; the member's go with their membership.
|
||||
class: SharedSpaceKey
|
||||
serverOnly: true
|
||||
table: shared_space_key
|
||||
fields:
|
||||
id: UuidValue?, defaultPersist=random_v7
|
||||
sharedSpaceId: UuidValue, relation(parent=shared_space, onDelete=Cascade)
|
||||
### The key's ID, which the rows it seals name in their `keyId`.
|
||||
keyId: String
|
||||
### Whom it is sealed to: the register's account ID.
|
||||
recipientId: UuidValue
|
||||
### The sealer's signature, then the sealed box.
|
||||
sealedKey: ByteData
|
||||
### Who sealed it: the register's account ID, whose signing key checks it.
|
||||
sealedBy: UuidValue
|
||||
createdAt: DateTime
|
||||
indexes:
|
||||
shared_space_key_unique_idx:
|
||||
fields: sharedSpaceId, keyId, recipientId
|
||||
unique: true
|
||||
shared_space_key_recipient_idx:
|
||||
fields: recipientId
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
### A key the caller holds that a member of the space lacks: for the
|
||||
### caller's device to seal to them (ADR 0044, D3).
|
||||
class: SharedSpaceKeyNeed
|
||||
fields:
|
||||
keyId: String
|
||||
### The member who lacks it.
|
||||
person: UuidValue
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
import 'package:clock/clock.dart';
|
||||
import 'package:halkyon_base_server/src/auth/scopes.dart';
|
||||
import 'package:halkyon_base_server/src/generated/protocol.dart';
|
||||
import 'package:halkyon_base_server/src/profile/profiles.dart';
|
||||
import 'package:halkyon_base_server/src/register/account_register.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
import 'package:serverpod_auth_idp_server/core.dart';
|
||||
|
||||
/// The data keys of a space you share, each sealed on a device to one
|
||||
/// member (ADR 0044, D3): the members' public keys to seal to and pin
|
||||
/// (D18), the keys a member still lacks, sealing them, and your own. The
|
||||
/// instance keeps what devices sealed and never opens it. Only for people
|
||||
/// with an identity; a space you aren't a member of can't be told from one
|
||||
/// that doesn't exist ([SharedSpaceNotFoundException]).
|
||||
class SharedSpaceKeysEndpoint extends Endpoint {
|
||||
@override
|
||||
Set<Scope> get requiredScopes => {identified};
|
||||
|
||||
/// The public keys of [space]'s members who have keys, you among them,
|
||||
/// as the register keeps them.
|
||||
Future<List<SharedSpaceMemberKeys>> memberKeys(
|
||||
Session session,
|
||||
UuidValue space,
|
||||
) async {
|
||||
final members = await _membersAsCaller(session, space);
|
||||
final people = {for (final member in members) member.authUserId};
|
||||
final (keys, profiles) = await (
|
||||
AccountRegister.of(session.serverpod).publicKeysOf(session, people),
|
||||
profilesOf(session, people),
|
||||
).wait;
|
||||
return [
|
||||
for (final member in members)
|
||||
if (keys[member.authUserId] case final keys?)
|
||||
SharedSpaceMemberKeys(
|
||||
person: member.authUserId,
|
||||
handle: profiles[member.authUserId]?.handle,
|
||||
encryption: keys.encryption,
|
||||
signing: keys.signing,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/// The keys of [space] you hold that members who have keys lack, for
|
||||
/// your device to seal to them; none while you may not seal ([seal]).
|
||||
Future<List<SharedSpaceKeyNeed>> keysNeeded(
|
||||
Session session,
|
||||
UuidValue space,
|
||||
) async {
|
||||
final person = _caller(session);
|
||||
final members = await _membersAsCaller(session, space);
|
||||
if (!_writes(members, person)) {
|
||||
return const [];
|
||||
}
|
||||
// SELECT … FROM "shared_space_key" WHERE "sharedSpaceId" = $1
|
||||
final sealed = await SharedSpaceKey.db.find(
|
||||
session,
|
||||
where: (t) => t.sharedSpaceId.equals(space),
|
||||
);
|
||||
final held = {
|
||||
for (final key in sealed)
|
||||
if (key.recipientId == person) key.keyId,
|
||||
};
|
||||
final keys = await AccountRegister.of(session.serverpod).publicKeysOf(
|
||||
session,
|
||||
{for (final member in members) member.authUserId},
|
||||
);
|
||||
return [
|
||||
for (final keyId in held.toList()..sort())
|
||||
for (final member in members)
|
||||
if (keys.containsKey(member.authUserId) &&
|
||||
!sealed.any(
|
||||
(key) =>
|
||||
key.keyId == keyId && key.recipientId == member.authUserId,
|
||||
))
|
||||
SharedSpaceKeyNeed(keyId: keyId, person: member.authUserId),
|
||||
];
|
||||
}
|
||||
|
||||
/// Keeps [keys], each sealed on your device to the member it names, as
|
||||
/// sealed by you; one a member has already stays as it was, as does the
|
||||
/// first of two the call names alike. Refused for a
|
||||
/// Viewer: only those who write, the Owner and Editors, seal keys, so a
|
||||
/// reader can't hand a newcomer a key of their own making.
|
||||
Future<void> seal(
|
||||
Session session,
|
||||
UuidValue space,
|
||||
List<SharedSpaceSealedKey> keys,
|
||||
) async {
|
||||
final person = _caller(session);
|
||||
final members = await _membersAsCaller(session, space);
|
||||
if (!_writes(members, person)) {
|
||||
throw SharedSpaceRefusedException(reason: SharedSpaceRefusal.notAllowed);
|
||||
}
|
||||
final memberIds = {for (final member in members) member.authUserId};
|
||||
if (keys.any((key) => !memberIds.contains(key.person))) {
|
||||
throw SharedSpaceNotFoundException(what: 'member');
|
||||
}
|
||||
final now = clock.now().toUtc();
|
||||
await session.db.transaction((transaction) async {
|
||||
// The space's row held, so two devices sealing at once take turns.
|
||||
// SELECT … FROM "shared_space" WHERE "id" = $1 FOR UPDATE
|
||||
await SharedSpace.db.findById(
|
||||
session,
|
||||
space,
|
||||
transaction: transaction,
|
||||
lockMode: LockMode.forUpdate,
|
||||
);
|
||||
// SELECT … FROM "shared_space_key" WHERE "sharedSpaceId" = $1
|
||||
final there = {
|
||||
for (final key in await SharedSpaceKey.db.find(
|
||||
session,
|
||||
where: (t) => t.sharedSpaceId.equals(space),
|
||||
transaction: transaction,
|
||||
))
|
||||
(key.keyId, key.recipientId),
|
||||
};
|
||||
final added = <(String, UuidValue)>{};
|
||||
// INSERT INTO "shared_space_key" (…) VALUES (…), …
|
||||
await SharedSpaceKey.db.insert(session, [
|
||||
for (final key in keys)
|
||||
if (!there.contains((key.keyId, key.person)) &&
|
||||
added.add((key.keyId, key.person)))
|
||||
SharedSpaceKey(
|
||||
sharedSpaceId: space,
|
||||
keyId: key.keyId,
|
||||
recipientId: key.person,
|
||||
sealedKey: key.sealedKey,
|
||||
sealedBy: person,
|
||||
createdAt: now,
|
||||
),
|
||||
], transaction: transaction);
|
||||
});
|
||||
}
|
||||
|
||||
/// The keys of [space] sealed to you, each naming who sealed it.
|
||||
Future<List<SharedSpaceSealedKey>> mine(
|
||||
Session session,
|
||||
UuidValue space,
|
||||
) async {
|
||||
final person = _caller(session);
|
||||
await _membersAsCaller(session, space);
|
||||
// SELECT … FROM "shared_space_key"
|
||||
// WHERE "sharedSpaceId" = $1 AND "recipientId" = $2
|
||||
return [
|
||||
for (final key in await SharedSpaceKey.db.find(
|
||||
session,
|
||||
where: (t) =>
|
||||
t.sharedSpaceId.equals(space) & t.recipientId.equals(person),
|
||||
orderBy: (t) => t.createdAt,
|
||||
))
|
||||
SharedSpaceSealedKey(
|
||||
keyId: key.keyId,
|
||||
person: key.sealedBy,
|
||||
sealedKey: key.sealedKey,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/// The members of [space] where the caller is one of them;
|
||||
/// [SharedSpaceNotFoundException] otherwise.
|
||||
Future<List<SharedSpaceMember>> _membersAsCaller(
|
||||
Session session,
|
||||
UuidValue space,
|
||||
) async {
|
||||
final person = _caller(session);
|
||||
// SELECT … FROM "shared_space_member" WHERE "sharedSpaceId" = $1
|
||||
// ORDER BY "joinedAt"
|
||||
final members = await SharedSpaceMember.db.find(
|
||||
session,
|
||||
where: (t) => t.sharedSpaceId.equals(space),
|
||||
orderBy: (t) => t.joinedAt,
|
||||
);
|
||||
if (!members.any((member) => member.authUserId == person)) {
|
||||
throw SharedSpaceNotFoundException(what: 'space');
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
/// Whether [person] writes in the space of [members]: its Owner or an
|
||||
/// Editor.
|
||||
bool _writes(List<SharedSpaceMember> members, UuidValue person) =>
|
||||
members.firstWhere((member) => member.authUserId == person).role !=
|
||||
SharedSpaceRole.viewer;
|
||||
|
||||
UuidValue _caller(Session session) => session.authenticated!.authUserId;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
### A member's public keys, as the register keeps them, which a device pins
|
||||
### the first time it sees them and seals keys to (ADR 0044, D3 and D18).
|
||||
### Named by the member's account ID, as the space's synced rows name their
|
||||
### authors: a device pins keys per person, across every space it shares
|
||||
### with them.
|
||||
class: SharedSpaceMemberKeys
|
||||
fields:
|
||||
person: UuidValue
|
||||
### Their handle; null without a public profile.
|
||||
handle: String?
|
||||
### X25519's, which keys are sealed to.
|
||||
encryption: ByteData
|
||||
### Ed25519's, which their signatures check against.
|
||||
signing: ByteData
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
### A key a device sealed to a member, as it sends it (ADR 0044, D3), or as
|
||||
### the member gets it back: [person] is whom it is sealed to on the way
|
||||
### in, and who sealed it on the way out.
|
||||
class: SharedSpaceSealedKey
|
||||
fields:
|
||||
keyId: String
|
||||
person: UuidValue
|
||||
### The sealer's signature, then the sealed box.
|
||||
sealedKey: ByteData
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue