feat: Initial MVP with home server identity model
- User model with Ed25519 keypair identity - Register/login on home server (creates keypair) - Join guest servers with signature verification - Public endpoints for cross-server identity - Channel and message CRUD - JWT authentication middleware Architecture: Home server model, no federation, client-driven sync
This commit is contained in:
commit
6a8977c4c3
14 files changed
+1494
No files matched your search
+126
@@ -0,0 +1,126 @@
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
|
||||
/// User entity - works for both HOME and GUEST servers
|
||||
///
|
||||
/// On HOME server:
|
||||
/// - isLocal = true
|
||||
/// - stores password, encrypted private key
|
||||
/// - this is where identity lives
|
||||
///
|
||||
/// On GUEST server:
|
||||
/// - isLocal = false
|
||||
/// - homeServer contains the URL of user's home
|
||||
/// - publicKey cached for verification
|
||||
@Entity()
|
||||
class User {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
// Global identifier: "username@homeserver.com"
|
||||
@Unique()
|
||||
String userId;
|
||||
|
||||
// Basic profile
|
||||
@Index()
|
||||
String username;
|
||||
String? displayName;
|
||||
String? avatarUrl;
|
||||
String? bio;
|
||||
|
||||
// Identity
|
||||
String publicKey; // Ed25519 public key, always stored
|
||||
|
||||
// HOME SERVER ONLY: Auth & key backup
|
||||
String? passwordHash;
|
||||
String? encryptedPrivateKey; // Encrypted with user's password
|
||||
String? keyPairSalt;
|
||||
|
||||
// Server relationship
|
||||
bool isLocal; // true = this is their home server
|
||||
String? homeServer; // if guest, where is their home? (e.g., "https://my.server.com")
|
||||
|
||||
// Server-specific
|
||||
List<String> roles; // ["admin", "moderator", "member"]
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime? lastSyncedAt; // When profile was last refreshed from home
|
||||
|
||||
User({
|
||||
required this.userId,
|
||||
required this.username,
|
||||
required this.publicKey,
|
||||
required this.isLocal,
|
||||
this.displayName,
|
||||
this.avatarUrl,
|
||||
this.bio,
|
||||
this.passwordHash,
|
||||
this.encryptedPrivateKey,
|
||||
this.keyPairSalt,
|
||||
this.homeServer,
|
||||
List<String>? roles,
|
||||
DateTime? createdAt,
|
||||
DateTime? lastSyncedAt,
|
||||
}) : roles = roles ?? ['member'],
|
||||
createdAt = createdAt ?? DateTime.now(),
|
||||
lastSyncedAt = lastSyncedAt;
|
||||
}
|
||||
|
||||
@Entity()
|
||||
class Channel {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
@Unique()
|
||||
String name;
|
||||
|
||||
String? description;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
Channel({
|
||||
required this.name,
|
||||
this.description,
|
||||
DateTime? createdAt,
|
||||
}) : createdAt = createdAt ?? DateTime.now();
|
||||
}
|
||||
|
||||
@Entity()
|
||||
class Message {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
int channelId;
|
||||
|
||||
// Author info - includes enough for cross-server profile fetch
|
||||
int authorLocalId;
|
||||
String authorUserId; // "alice@homeserver.com"
|
||||
String? authorHomeServer; // for profile fetching
|
||||
|
||||
// Cached for display (snapshot at message time)
|
||||
String authorUsername;
|
||||
String? authorAvatarUrl;
|
||||
|
||||
String content;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime? editedAt;
|
||||
|
||||
Message({
|
||||
required this.channelId,
|
||||
required this.authorLocalId,
|
||||
required this.authorUserId,
|
||||
required this.authorUsername,
|
||||
required this.content,
|
||||
this.authorHomeServer,
|
||||
this.authorAvatarUrl,
|
||||
DateTime? createdAt,
|
||||
this.editedAt,
|
||||
}) : createdAt = createdAt ?? DateTime.now();
|
||||
}
|
||||
Reference in new issue
Block a user