Skip to content

Reference v0.6 sqlite multiuser

github-actions[bot] edited this page Dec 6, 2025 · 1 revision

v0.6 Specification: SQLite Migration & Multi-User Support

Status: Implemented

Overview

This version introduces a complete Domain-Driven Design (DDD) architecture, migrates from JSON files to SQLite, and implements a multi-user system with role-based access control.

Architecture

Bounded Contexts

┌─────────────────────────────────────────────────────────────┐
│                    Identity & Access                         │
│  ┌─────────────────┐  ┌─────────────┐  ┌────────────────┐   │
│  │  Organization   │  │    User     │  │      Role      │   │
│  │  (Aggregate)    │  │ (Aggregate) │  │   (Aggregate)  │   │
│  └─────────────────┘  └─────────────┘  └────────────────┘   │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                       Deployment                             │
│  ┌─────────────────┐  ┌─────────────────────────────────┐   │
│  │   Environment   │  │          Deployment             │   │
│  │   (Aggregate)   │  │          (Aggregate)            │   │
│  └─────────────────┘  └─────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                    Stack Management                          │
│  ┌─────────────────┐  ┌─────────────────────────────────┐   │
│  │   StackSource   │  │       StackDefinition           │   │
│  │   (Aggregate)   │  │       (Read Model)              │   │
│  └─────────────────┘  └─────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

Project Structure (Implemented)

Domain Layer

src/ReadyStackGo.Domain/
├── SharedKernel/                          # DDD Building Blocks
│   ├── AggregateRoot.cs                   # Base for Aggregate Roots
│   ├── Entity.cs                          # Base for Entities
│   ├── ValueObject.cs                     # Base for Value Objects
│   ├── DomainEvent.cs                     # Base for Domain Events
│   ├── IDomainEvent.cs                    # Interface for Events
│   └── AssertionConcern.cs                # Validation Helper Methods
│
├── IdentityAccess/                        # Identity & Access Context
│   ├── Organizations/                     # Organization Aggregate
│   │   ├── Organization.cs                # Aggregate Root
│   │   ├── OrganizationId.cs              # Typed ID
│   │   ├── OrganizationProvisioned.cs     # Domain Event
│   │   ├── OrganizationActivated.cs       # Domain Event
│   │   ├── OrganizationDeactivated.cs     # Domain Event
│   │   ├── OrganizationProvisioningService.cs  # Domain Service
│   │   └── IOrganizationRepository.cs     # Repository Interface
│   │
│   ├── Users/                             # User Aggregate
│   │   ├── User.cs                        # Aggregate Root
│   │   ├── UserId.cs                      # Typed ID
│   │   ├── EmailAddress.cs                # Value Object
│   │   ├── HashedPassword.cs              # Value Object
│   │   ├── Enablement.cs                  # Value Object
│   │   ├── RoleAssignment.cs              # Value Object
│   │   ├── IPasswordHasher.cs             # Interface
│   │   ├── UserRegistered.cs              # Domain Event
│   │   ├── UserRoleAssigned.cs            # Domain Event
│   │   ├── UserRoleRevoked.cs             # Domain Event
│   │   ├── UserPasswordChanged.cs         # Domain Event
│   │   ├── UserEnablementChanged.cs       # Domain Event
│   │   ├── AuthenticationService.cs       # Domain Service
│   │   ├── SystemAdminRegistrationService.cs  # Domain Service
│   │   └── IUserRepository.cs             # Repository Interface
│   │
│   └── Roles/                             # Role Aggregate
│       ├── Role.cs                        # Aggregate Root (with Built-in Roles)
│       ├── RoleId.cs                      # Typed ID
│       ├── Permission.cs                  # Value Object
│       ├── ScopeType.cs                   # Enum (Global, Organization, Environment)
│       └── IRoleRepository.cs             # Repository Interface
│
├── Deployment/                            # Deployment Context
│   ├── Environments/                      # Environment Aggregate
│   │   ├── Environment.cs                 # Aggregate Root
│   │   ├── EnvironmentId.cs               # Typed ID
│   │   ├── EnvironmentType.cs             # Enum
│   │   ├── ConnectionConfig.cs            # Value Object
│   │   ├── EnvironmentCreated.cs          # Domain Event
│   │   └── IEnvironmentRepository.cs      # Repository Interface
│   │
│   └── Deployments/                       # Deployment Aggregate
│       ├── Deployment.cs                  # Aggregate Root
│       ├── DeploymentId.cs                # Typed ID
│       ├── DeploymentStatus.cs            # Enum
│       ├── DeployedService.cs             # Entity
│       ├── DeploymentStarted.cs           # Domain Event
│       ├── DeploymentCompleted.cs         # Domain Event
│       └── IDeploymentRepository.cs       # Repository Interface
│
└── StackManagement/                       # Stack Management Context
    └── StackSources/
        ├── StackSource.cs                 # Aggregate Root
        ├── StackSourceId.cs               # Typed ID
        ├── StackSourceType.cs             # Enum
        ├── StackDefinition.cs             # Value Object
        ├── StackVariable.cs               # Value Object
        ├── IStackSourceRepository.cs      # Repository Interface
        └── IStackDefinitionRepository.cs  # Repository Interface

Application Layer

src/ReadyStackGo.Application/
├── DependencyInjection.cs                 # MediatR Registration
│
├── Services/                              # Application Service Interfaces
│   ├── IDockerService.cs                  # Docker Engine Abstraction
│   ├── IDockerComposeParser.cs            # Compose YAML Parser
│   ├── IDeploymentService.cs              # Deployment Orchestration
│   ├── IEnvironmentService.cs             # Environment Management
│   ├── IStackSourceService.cs             # Stack Source Management
│   ├── IStackSourceProvider.cs            # Provider Pattern
│   ├── IStackCache.cs                     # In-Memory Cache
│   └── ITokenService.cs                   # JWT Token Generation
│
└── UseCases/                              # CQRS with MediatR
    ├── Administration/
    │   └── RegisterSystemAdmin/           # Initial Admin Registration
    │       ├── RegisterSystemAdminCommand.cs
    │       └── RegisterSystemAdminHandler.cs
    │
    ├── Authentication/
    │   └── Login/                         # User Login
    │       ├── LoginCommand.cs
    │       └── LoginHandler.cs
    │
    ├── Organizations/
    │   └── ProvisionOrganization/         # Create Org with Admin
    │       ├── ProvisionOrganizationCommand.cs
    │       └── ProvisionOrganizationHandler.cs
    │
    ├── Environments/
    │   ├── CreateEnvironment/             # Create Environment
    │   ├── UpdateEnvironment/             # Update Environment
    │   ├── DeleteEnvironment/             # Delete Environment
    │   ├── GetEnvironment/                # Load Single Environment
    │   ├── ListEnvironments/              # List All Environments
    │   ├── SetDefaultEnvironment/         # Set Default
    │   ├── TestConnection/                # Test Docker Connection
    │   └── EnvironmentDtos.cs             # Data Transfer Objects
    │
    ├── Deployments/
    │   ├── DeployCompose/                 # Deploy Stack
    │   ├── RemoveDeployment/              # Remove Stack
    │   ├── GetDeployment/                 # Deployment Details
    │   ├── ListDeployments/               # All Deployments
    │   ├── ParseCompose/                  # Parse Compose YAML
    │   └── DeploymentDtos.cs              # DTOs incl. DeploymentPlan
    │
    ├── Containers/
    │   ├── ListContainers/                # List Containers
    │   ├── StartContainer/                # Start Container
    │   ├── StopContainer/                 # Stop Container
    │   └── ContainerDto.cs                # Container DTOs
    │
    ├── Stacks/
    │   ├── GetStack/                      # Load Stack Definition
    │   └── ListStacks/                    # Available Stacks
    │
    ├── StackSources/
    │   ├── ListStackSources/              # Configured Sources
    │   └── SyncStackSources/              # Sync Sources
    │
    ├── Dashboard/
    │   └── GetDashboardStats/             # Dashboard Statistics
    │
    └── Wizard/
        ├── GetWizardStatus/               # Query Wizard Status
        ├── CompleteWizard/                # Complete Wizard
        └── WizardDtos.cs                  # Wizard DTOs

Infrastructure Layer

src/ReadyStackGo.Infrastructure/
├── DependencyInjection.cs                 # Service Registration
│
├── Authentication/                        # Authentication
│   ├── BCryptPasswordHasher.cs            # BCrypt Implementation
│   ├── TokenService.cs                    # JWT Token Service
│   └── JwtSettings.cs                     # JWT Configuration
│
├── Configuration/                         # JSON-based Config (reduced)
│   ├── IConfigStore.cs                    # Interface
│   ├── ConfigStore.cs                     # Implementation
│   ├── SystemConfig.cs                    # System Settings
│   ├── TlsConfig.cs                       # TLS Certificates
│   ├── FeaturesConfig.cs                  # Feature Flags
│   ├── ReleaseConfig.cs                   # Installed Version
│   ├── WizardState.cs                     # Wizard State
│   └── DeploymentMode.cs                  # Enum
│
├── DataAccess/                            # SQLite with EF Core
│   ├── ReadyStackGoDbContext.cs           # EF DbContext
│   ├── Configurations/                    # EF Fluent API Mappings
│   │   ├── OrganizationConfiguration.cs
│   │   ├── UserConfiguration.cs
│   │   ├── EnvironmentConfiguration.cs
│   │   └── DeploymentConfiguration.cs
│   └── Repositories/                      # Repository Implementations
│       ├── OrganizationRepository.cs
│       ├── UserRepository.cs
│       ├── RoleRepository.cs
│       ├── EnvironmentRepository.cs
│       └── DeploymentRepository.cs
│
├── Docker/                                # Docker Engine Integration
│   └── DockerService.cs                   # Docker.DotNet Wrapper
│
├── Manifests/                             # Release Manifests
│   ├── IManifestProvider.cs
│   ├── ManifestProvider.cs
│   ├── ReleaseManifest.cs
│   └── DockerComposeParser.cs             # YAML Parser
│
├── Services/                              # Business Services
│   ├── IDeploymentEngine.cs               # Interface
│   ├── DeploymentEngine.cs                # Deployment Orchestration
│   ├── DeploymentService.cs               # High-Level Service
│   └── EnvironmentService.cs              # Environment Operations
│
├── Stacks/                                # Stack Source Management
│   ├── StackSourceService.cs              # Source Management
│   ├── InMemoryStackCache.cs              # Stack Cache
│   ├── Configuration/
│   │   └── StackSourceConfig.cs           # appsettings.json Config
│   └── Sources/
│       └── LocalDirectoryStackSourceProvider.cs
│
└── Tls/                                   # TLS Certificate Management
    ├── ITlsService.cs
    └── TlsService.cs

Domain Model (Implemented)

IdentityAccess Context

Organization (Aggregate Root)

public class Organization : AggregateRoot<OrganizationId>
{
    public OrganizationId Id { get; private set; }
    public string Name { get; private set; }
    public string Description { get; private set; }
    public bool Active { get; private set; }
    public DateTime CreatedAt { get; private set; }
    public DateTime? UpdatedAt { get; private set; }

    // Factory method
    public static Organization Provision(OrganizationId id, string name, string description)

    // Behaviors
    public void Activate()
    public void Deactivate()
    public void UpdateDescription(string description)
}

User (Aggregate Root)

public class User : AggregateRoot<UserId>
{
    public UserId Id { get; private set; }
    public string Username { get; private set; }
    public EmailAddress Email { get; private set; }
    public HashedPassword Password { get; private set; }
    public Enablement Enablement { get; private set; }
    public DateTime CreatedAt { get; private set; }
    public DateTime? UpdatedAt { get; private set; }

    private readonly List<RoleAssignment> _roleAssignments;
    public IReadOnlyCollection<RoleAssignment> RoleAssignments => _roleAssignments.AsReadOnly();

    // Factory method
    public static User Register(UserId id, string username, EmailAddress email, HashedPassword password)

    // Behaviors
    public void AssignRole(RoleAssignment assignment)
    public void RevokeRole(RoleId roleId, ScopeType scopeType, string? scopeId)
    public void ChangePassword(HashedPassword newPassword)
    public void Enable()
    public void Disable()
    public bool HasPermission(Permission permission, ScopeType scopeType, string? scopeId)
    public RoleId? GetPrimaryRole()
}

Note: User is no longer bound to an Organization (unlike the original Tenant concept). Instead, permissions are controlled via RoleAssignments with Scopes.

Role (Aggregate with Built-in Roles)

public class Role : AggregateRoot<RoleId>
{
    public RoleId Id { get; private set; }
    public string Name { get; private set; }
    public string Description { get; private set; }
    public ScopeType AllowedScopes { get; private set; }
    public IReadOnlyCollection<Permission> Permissions { get; }

    // Built-in Roles (read-only)
    public static RoleId SystemAdmin { get; }   // Global Admin
    public static RoleId OrgOwner { get; }      // Organization Owner
    public static RoleId Operator { get; }      // Stack Operator
    public static RoleId Viewer { get; }        // Read-Only
}

[Flags]
public enum ScopeType
{
    Global = 1,
    Organization = 2,
    Environment = 4
}

Deployment Context

Environment (Aggregate Root)

public class Environment : AggregateRoot<EnvironmentId>
{
    public EnvironmentId Id { get; private set; }
    public OrganizationId OrganizationId { get; private set; }
    public string Name { get; private set; }
    public string Description { get; private set; }
    public EnvironmentType Type { get; private set; }
    public string ConnectionString { get; private set; }
    public bool IsDefault { get; private set; }
    public DateTime CreatedAt { get; private set; }

    // Factory methods
    public static Environment CreateDockerSocket(EnvironmentId id, OrganizationId orgId,
        string name, string description, string socketPath)
    public static Environment CreateDockerApi(EnvironmentId id, OrganizationId orgId,
        string name, string description, string apiUrl)

    // Behaviors
    public void SetAsDefault()
    public void UnsetAsDefault()
    public void Update(string name, string description, string connectionString)
}

public enum EnvironmentType
{
    DockerSocket = 0,
    DockerApi = 1
}

Deployment (Aggregate Root)

public class Deployment : AggregateRoot<DeploymentId>
{
    public DeploymentId Id { get; private set; }
    public EnvironmentId EnvironmentId { get; private set; }
    public string StackName { get; private set; }
    public string ProjectName { get; private set; }
    public DeploymentStatus Status { get; private set; }
    public string? ErrorMessage { get; private set; }
    public DateTime CreatedAt { get; private set; }
    public DateTime? CompletedAt { get; private set; }

    private readonly List<DeployedService> _services;
    public IReadOnlyCollection<DeployedService> Services => _services.AsReadOnly();

    // Factory method
    public static Deployment Start(DeploymentId id, EnvironmentId envId,
        string stackName, string projectName)

    // Behaviors
    public void MarkAsRunning(IEnumerable<DeployedService> services)
    public void MarkAsFailed(string errorMessage)
    public void MarkAsStopped()
    public void MarkAsRemoved()
}

public enum DeploymentStatus
{
    Pending = 0,
    Running = 1,
    Stopped = 2,
    Failed = 3,
    Removed = 4
}

StackManagement Context

StackSource (Aggregate Root)

public class StackSource
{
    public StackSourceId Id { get; }
    public string Name { get; }
    public StackSourceType Type { get; }
    public string Configuration { get; }  // JSON
    public bool Enabled { get; }
    public DateTime? LastSyncAt { get; }
}

public enum StackSourceType
{
    LocalDirectory = 0,
    GitRepository = 1
}

StackDefinition (Value Object)

public class StackDefinition
{
    public string Id { get; }
    public string Name { get; }
    public string Description { get; }
    public string Category { get; }
    public string ComposeContent { get; }
    public IReadOnlyList<StackVariable> Variables { get; }
    public string SourceId { get; }
}

Domain Services

AuthenticationService

public class AuthenticationService
{
    private readonly IUserRepository _userRepository;
    private readonly IPasswordHasher _passwordHasher;

    public User? Authenticate(string username, string password)
    {
        var user = _userRepository.FindByUsername(username);
        if (user == null) return null;
        if (!user.Enablement.IsEnabled) return null;
        if (!user.Password.Verify(password, _passwordHasher)) return null;
        return user;
    }
}

SystemAdminRegistrationService

public class SystemAdminRegistrationService
{
    private readonly IUserRepository _userRepository;
    private readonly IPasswordHasher _passwordHasher;

    public User RegisterSystemAdmin(UserId userId, string username, string email, string password)
    {
        // Validation: only one SystemAdmin allowed
        if (_userRepository.AnyUserExists())
            throw new InvalidOperationException("System admin already exists");

        var hashedPassword = HashedPassword.Create(password, _passwordHasher);
        var user = User.Register(userId, username, new EmailAddress(email), hashedPassword);
        user.AssignRole(RoleAssignment.Global(RoleId.SystemAdmin));

        _userRepository.Add(user);
        return user;
    }
}

OrganizationProvisioningService

public class OrganizationProvisioningService
{
    private readonly IOrganizationRepository _organizationRepository;

    public Organization ProvisionOrganization(OrganizationId id, string name, string description)
    {
        // Validation
        if (_organizationRepository.ExistsByName(name))
            throw new InvalidOperationException($"Organization '{name}' already exists");

        var org = Organization.Provision(id, name, description);
        org.Activate();

        _organizationRepository.Add(org);
        return org;
    }
}

Application Layer UseCases

Administration

UseCase Type Description
RegisterSystemAdmin Command Registers the initial System Administrator

Authentication

UseCase Type Description
Login Command Authenticates User and returns JWT

Organizations

UseCase Type Description
ProvisionOrganization Command Creates a new Organization

Environments

UseCase Type Description
CreateEnvironment Command Creates a new Environment
UpdateEnvironment Command Updates Environment settings
DeleteEnvironment Command Deletes an Environment
SetDefaultEnvironment Command Sets Default Environment
TestConnection Command Tests Docker connection
GetEnvironment Query Loads an Environment
ListEnvironments Query Lists all Environments

Deployments

UseCase Type Description
DeployCompose Command Deploys a Stack from Compose YAML
RemoveDeployment Command Removes a Stack
ParseCompose Command Parses Compose YAML for Preview
GetDeployment Query Loads Deployment details
ListDeployments Query Lists all Deployments

Containers

UseCase Type Description
StartContainer Command Starts a Container
StopContainer Command Stops a Container
ListContainers Query Lists all Containers

Wizard

UseCase Type Description
CompleteWizard Command Completes Wizard, creates Org+Admin
GetWizardStatus Query Checks if Wizard is completed

Roles & Permissions

Role Scope Description Permissions
SystemAdmin Global System Administrator Everything
OrgOwner Organization Org Owner Users, Environments, Stacks within the Org
Operator Org/Environment Stack Operator Deploy, Start, Stop, Remove Stacks
Viewer Org/Environment Read Only Read all resources in Scope

Configuration

SQLite (EF Core)

All dynamic data is stored in SQLite:

  • Organizations
  • Users (incl. RoleAssignments)
  • Environments
  • Deployments

JSON Files (reduced)

Only static configuration in JSON:

File Content
rsgo.system.json BaseUrl, Ports, Network, WizardState
rsgo.tls.json TLS Certificates
rsgo.features.json Feature Flags
rsgo.release.json Installed Stack Version

Removed since v0.6:

  • rsgo.contexts.json (obsolete since v0.4)
  • rsgo.security.json (replaced by SQLite Users)
  • rsgo.organization.json (replaced by SQLite Organizations)

Implementation Progress

Phase 1: Domain Layer ✅

  • SharedKernel (AggregateRoot, Entity, ValueObject, DomainEvent)
  • IdentityAccess Context (Organization, User, Role)
  • Deployment Context (Environment, Deployment)
  • StackManagement Context (StackSource, StackDefinition)
  • Reqnroll Tests for Domain Logic

Phase 2: Infrastructure Layer ✅

  • EF Core DbContext with SQLite
  • Repository Implementations
  • Entity Configurations (Fluent API)
  • Integration Tests

Phase 3: Application Layer ✅

  • MediatR for CQRS
  • UseCase Handlers
  • DTOs
  • Wizard Integration

Phase 4: Cleanup ✅

  • Obsolete ContextsConfig removed
  • Infrastructure Layer reorganized (technical structure)
  • Documentation updated

Tests

Domain Tests (Reqnroll/SpecFlow)

tests/ReadyStackGo.IntegrationTests/
├── Features/
│   ├── OrganizationProvisioning.feature
│   ├── UserAuthentication.feature
│   ├── UserRoleManagement.feature
│   └── EnvironmentManagement.feature
└── StepDefinitions/
    └── ...

Unit Tests

tests/ReadyStackGo.UnitTests/
├── DeploymentEngineTests.cs
├── Stacks/
│   ├── LocalDirectoryStackSourceProviderTests.cs
│   └── VariablePriorityTests.cs
└── ...

Open Items (for v0.7+)

  • JWT Token Refresh Strategy
  • Audit Logging for Security Events
  • Password Reset Flow
  • User Self-Service
  • Multi-Environment Deployments
  • GitRepository StackSourceProvider
  • Deployment Rollback (save and restore previous stack version)

Clone this wiki locally