-
Notifications
You must be signed in to change notification settings - Fork 0
Reference v0.6 sqlite multiuser
github-actions[bot] edited this page Dec 6, 2025
·
1 revision
Status: Implemented
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.
┌─────────────────────────────────────────────────────────────┐
│ Identity & Access │
│ ┌─────────────────┐ ┌─────────────┐ ┌────────────────┐ │
│ │ Organization │ │ User │ │ Role │ │
│ │ (Aggregate) │ │ (Aggregate) │ │ (Aggregate) │ │
│ └─────────────────┘ └─────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Deployment │
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │ Environment │ │ Deployment │ │
│ │ (Aggregate) │ │ (Aggregate) │ │
│ └─────────────────┘ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Stack Management │
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │ StackSource │ │ StackDefinition │ │
│ │ (Aggregate) │ │ (Read Model) │ │
│ └─────────────────┘ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
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
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
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
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)
}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.
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
}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
}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
}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
}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; }
}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;
}
}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;
}
}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;
}
}| UseCase | Type | Description |
|---|---|---|
| RegisterSystemAdmin | Command | Registers the initial System Administrator |
| UseCase | Type | Description |
|---|---|---|
| Login | Command | Authenticates User and returns JWT |
| UseCase | Type | Description |
|---|---|---|
| ProvisionOrganization | Command | Creates a new Organization |
| 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 |
| 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 |
| UseCase | Type | Description |
|---|---|---|
| StartContainer | Command | Starts a Container |
| StopContainer | Command | Stops a Container |
| ListContainers | Query | Lists all Containers |
| UseCase | Type | Description |
|---|---|---|
| CompleteWizard | Command | Completes Wizard, creates Org+Admin |
| GetWizardStatus | Query | Checks if Wizard is completed |
| 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 |
All dynamic data is stored in SQLite:
- Organizations
- Users (incl. RoleAssignments)
- Environments
- Deployments
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)
- SharedKernel (AggregateRoot, Entity, ValueObject, DomainEvent)
- IdentityAccess Context (Organization, User, Role)
- Deployment Context (Environment, Deployment)
- StackManagement Context (StackSource, StackDefinition)
- Reqnroll Tests for Domain Logic
- EF Core DbContext with SQLite
- Repository Implementations
- Entity Configurations (Fluent API)
- Integration Tests
- MediatR for CQRS
- UseCase Handlers
- DTOs
- Wizard Integration
- Obsolete ContextsConfig removed
- Infrastructure Layer reorganized (technical structure)
- Documentation updated
tests/ReadyStackGo.IntegrationTests/
├── Features/
│ ├── OrganizationProvisioning.feature
│ ├── UserAuthentication.feature
│ ├── UserRoleManagement.feature
│ └── EnvironmentManagement.feature
└── StepDefinitions/
└── ...
tests/ReadyStackGo.UnitTests/
├── DeploymentEngineTests.cs
├── Stacks/
│ ├── LocalDirectoryStackSourceProviderTests.cs
│ └── VariablePriorityTests.cs
└── ...
- 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)
Getting Started
Architecture
Configuration
Security
Setup Wizard
Development
Operations
CI/CD
Reference
- Roadmap
- API Reference
- Configuration Reference
- Manifest Schema
- Multi-Environment
- Stack Sources
- Plugin System
- Technical Specification
- Full Specification
Specifications
Release Notes