9385dd253a327c6d914a8f8500eeae89d39b8aff
The hook carried ~50 lines of gitleaks logic and a comment explaining it was self-contained because "this repo has no Makefile". It has one now, so the reason is gone and the arrangement is backwards: a hook is a trigger, and logic belongs where it can be read, run by hand, and changed under review. .githooks/pre-push is now a byte-identical shim onto `make pre-push` in every repo in the workspace. The scan itself moves to ci/secrets.sh unchanged, and `make secrets` runs it on its own. The call surface is identical everywhere; what it runs is not, and should not be — each repo gates what it actually has. That is the point of standardising the name rather than the contents: nobody has to read a repo to find out how to check it. secrets runs first, deliberately. It is the only failure here that cannot be undone by fixing it afterwards — a failed lint costs another commit, a pushed credential is cached and indexed whether or not it is later deleted. Some of these gates fail today, on lint debt that predates them, and they are left wired anyway. The board was measured once and written down in T-56 instead of being worked around here. Narrowing each gate to whatever already passes would produce a gate that reports success for doing nothing, which is the failure this workspace keeps rediscovering. Co-Authored-By: Claude <noreply@anthropic.com>
Tatlock UI
A Flutter-based unified homelab dashboard for the Tower of Joy infrastructure, replacing Organizr at home.schweitz.net.
Overview
Tatlock UI provides a single interface for:
- Dashboard: System metrics, service status, quick actions
- Tatlock Chat: LLM conversation interface with streaming responses
- Infrastructure: Container management, logs, resource monitoring
- Housekeeping: Home automation control via Home Assistant
See PHILOSOPHY.md for the guiding vision.
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Flutter 3.38+ |
| State Management | Riverpod 2.x with code generation |
| Routing | go_router |
| HTTP Client | Dio + Retrofit |
| Authentication | Authentik OIDC via flutter_appauth |
| Theming | flex_color_scheme (Material 3) |
| Code Generation | freezed, json_serializable, riverpod_generator |
Platforms
| Platform | Status | Notes |
|---|---|---|
| Web | Primary | Docker deployment at port 8092 |
| Android | Supported | For mobile access |
| iOS | Supported | For mobile access |
| macOS | Supported | Native desktop |
| Linux | Supported | Native desktop |
| Windows | Supported | Native desktop |
Architecture
This project follows Clean Architecture with feature-based organization:
lib/
├── app.dart # MaterialApp.router setup
├── main.dart # Entry point
├── core/ # Shared infrastructure
│ ├── api/ # HTTP client, interceptors, SSE
│ ├── auth/ # Authentik OIDC integration
│ ├── config/ # Environment, service locator
│ └── theme/ # Material 3 theming
├── routing/ # go_router configuration
├── shared/ # Reusable components
│ ├── components/ # DataGrid, widgets
│ └── layouts/ # App scaffold, navigation
└── features/ # Feature modules
├── dashboard/
├── chat/
├── containers/
└── housekeeping/
Each feature follows the three-layer pattern:
features/{feature}/
├── presentation/ # UI Layer
│ ├── pages/ # Full screen widgets
│ ├── widgets/ # Feature-specific widgets
│ └── providers/ # Riverpod providers
├── domain/ # Business Logic Layer
│ ├── entities/ # Immutable business objects
│ ├── repositories/ # Abstract interfaces
│ └── usecases/ # Single-purpose operations
└── data/ # Data Layer
├── models/ # JSON serializable DTOs
├── datasources/ # API clients
└── repositories/ # Repository implementations
Layer Rules:
- Domain layer has NO dependencies on Flutter or external packages
- Data layer implements domain interfaces
- Presentation layer only depends on domain layer
- Cross-layer communication via dependency injection (Riverpod)
Backend APIs
| Service | URL | Purpose |
|---|---|---|
| Core API | https://api.schweitz.net |
Infrastructure, widgets, housekeeping |
| Tatlock API | https://tatlock.schweitz.net |
LLM chat completions, streaming |
Both APIs are behind Authentik SSO.
Key Endpoints:
GET /infrastructure/containers- Container list and managementGET /infrastructure/resources/system- CPU, memory, disk metricsGET /housekeeping/devices- Home Assistant devicesPOST /v1/chat/completions- Tatlock LLM (SSE streaming)
Development Setup
Prerequisites
- Flutter SDK 3.38+
- Dart SDK 3.10+
- Java 17+ (required for Android builds with Gradle 8.14+)
- For mobile: Android Studio / Xcode 26+
- For desktop: Platform-specific toolchains
Getting Started
# Clone the repository
git clone ssh://git@git.schweitz.net:2222/jpmschweitzer/tatlock-ui.git
cd tatlock-ui
# Install dependencies
flutter pub get
# Generate code (freezed, json_serializable, riverpod)
dart run build_runner build --delete-conflicting-outputs
# Run development server
flutter run -d chrome # Web
flutter run -d macos # macOS
flutter run # Default device
Code Generation
Run build_runner after modifying:
@freezed,@JsonSerializable, or@riverpodannotated codepubspec.yamlversion (auto-generateslib/version.g.dart)
dart run build_runner build --delete-conflicting-outputs
# Or watch mode during development
dart run build_runner watch --delete-conflicting-outputs
Generated files:
*.freezed.dart- Freezed immutable classes*.g.dart- JSON serialization, Riverpod providerslib/version.g.dart- App version from pubspec.yaml
Deployment
Web (Docker)
# Build the image
docker build -t git.schweitz.net/jpmschweitzer/tatlock-ui:latest .
# Or use the Gitea CI/CD pipeline
git push origin main
Deployment target: Port 8092, proxied via NPM at home.schweitz.net
Portainer Stack
version: '3.8'
services:
tatlock-ui:
image: git.schweitz.net/jpmschweitzer/tatlock-ui:latest
container_name: tatlock-ui
restart: unless-stopped
ports:
- "8092:80"
networks:
- docker-dataplane
networks:
docker-dataplane:
external: true
Navigation Structure
├── Homepage (Dashboard)
├── Tatlock (Chat)
├── Jellyfin (external)
├── SearXNG (external)
├── AMP
│ └── AMP Home
├── Coding
│ ├── Cloud IDE (external)
│ └── Gitea (external)
├── Infrastructure
│ ├── Containers
│ ├── Netdata (external)
│ ├── Portainer (external)
│ └── Proxy Manager (external)
├── Housekeeping
├── API Docs (external)
└── Settings
Authentication
Uses Authentik OIDC with the following configuration:
| Setting | Value |
|---|---|
| Application | tatlock-ui |
| Client Type | Public |
| Redirect URIs | https://home.schweitz.net/callback, net.schweitz.tatlock://callback, http://localhost:*/callback |
Project Links
- Repository: git.schweitz.net/jpmschweitzer/tatlock-ui
- Deployment: home.schweitz.net (replaces Organizr)
- Core API Docs: api.schweitz.net/docs
- Tatlock API Docs: household.schweitz.net/docs
Related Repositories
- tatlock: Backend LLM orchestration service
- core-api: Infrastructure and housekeeping API
- portainer-core: Docker Swarm stack definitions
License
Private - All rights reserved
Releases
5
Release v1.7.1
Latest
Languages
Dart
92.2%
C++
3.1%
CMake
2.4%
Shell
0.6%
Makefile
0.5%
Other
1.2%