Jacob Schmidt d0fac4199a Initial commit: Journal.Core library + Sidecar console app
- Fragment model with validation, DTOs (immutable records), repository, service
- Sidecar stdin/stdout JSON protocol for Tauri integration
- DI wiring via ServiceCollectionExtensions
- Scaffolded Journal.Api (not yet wired)

Co-Authored-By: Warp <agent@warp.dev>
2026-02-21 02:01:00 -06:00

Journal Backend (.NET)

A .NET 10 backend for the Project Journal app. Provides core journal functionality as a class library with a sidecar console app for Tauri integration and an optional HTTP API.

Project Structure

backend/
├── Journal.Core/              Class library — all business logic
│   ├── Models/
│   │   ├── Fragment.cs            Domain model (validated, owns Guid ID)
│   │   └── Command.cs            Stdin command shape for sidecar protocol
│   ├── Dtos/
│   │   └── FragmentDtos.cs        Immutable records for API boundary
│   │       ├── FragmentDto            Read (what goes out)
│   │       ├── CreateFragmentDto      Create (what comes in)
│   │       └── UpdateFragmentDto      Update (partial, all fields optional)
│   ├── Repositories/
│   │   ├── IFragmentRepository.cs     Interface (data access contract)
│   │   └── InMemoryFragmentRepository.cs  In-memory implementation
│   ├── Services/
│   │   ├── IFragmentService.cs        Interface (business logic contract)
│   │   └── FragmentService.cs         Validates, calls repo, maps to DTOs
│   ├── Entry.cs                   Command dispatcher (stdin/stdout)
│   ├── ServiceCollectionExtensions.cs  DI registration helper
│   └── Journal.Core.csproj
│
├── Journal.Sidecar/           Console app — Tauri sidecar bridge
│   ├── App.cs                     Boots DI container, runs Entry.RunAsync()
│   └── Journal.Sidecar.csproj     References Journal.Core
│
├── Journal.Api/               Web API — HTTP endpoint wrapper (optional)
│   ├── Program.cs
│   └── Journal.Api.csproj
│
└── README.md

Architecture

Each layer only knows about the one below it:

Sidecar (stdin/stdout)  ──┐
                          ├──►  Services (business logic)  ──►  Repositories (data access)
API (HTTP/JSON)  ─────────┘
  • Models — Domain objects with validation. The source of truth.
  • DTOs — Immutable records that cross the API boundary. Internal logic never leaks out.
  • Repositories — Where data lives. Swap InMemoryFragmentRepository for SQLite/EF Core later without touching anything above.
  • Services — Business rules, validation, orchestration. Doesn't know about HTTP or stdin.
  • Entry — Transport adapter. Translates stdin/stdout JSON into service calls.

Dependencies

  • Journal.CoreMicrosoft.Extensions.DependencyInjection.Abstractions (interface-only, lightweight)
  • Journal.SidecarMicrosoft.Extensions.DependencyInjection (full container implementation) + references Journal.Core

Building

# Build everything (building Sidecar also rebuilds Core if changed)
dotnet build backend\Journal.Sidecar\Journal.Sidecar.csproj

# Build just the library
dotnet build backend\Journal.Core\Journal.Core.csproj

# Format code
dotnet format backend\Journal.Core\Journal.Core.csproj

Publishing

Publish as a single-file self-contained executable (no .NET runtime install needed):

dotnet publish backend\Journal.Sidecar\Journal.Sidecar.csproj -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true

Output: backend\Journal.Sidecar\bin\Release\net10.0\win-x64\publish\Journal.Sidecar.exe (~70MB, everything bundled)

To exclude debug symbols: add -p:DebugType=none

For a smaller build that requires .NET 10 on the target machine:

dotnet publish backend\Journal.Sidecar\Journal.Sidecar.csproj -c Release -r win-x64 -p:PublishSingleFile=true

Sidecar Protocol

The sidecar communicates over stdin/stdout using JSON lines. One JSON line in, one JSON line out.

Command Format

{
  "action": "fragments.create",
  "id": null,
  "type": null,
  "tag": null,
  "payload": { "type": "!TRIGGER", "description": "stomach drop" }
}

Fields:

  • action — The operation to perform (e.g. fragments.list, fragments.create)
  • id — Target entity ID (for get/update/delete)
  • type / tag — Filter parameters (for search)
  • payload — Request body, deserialized into the appropriate DTO per action

Available Actions

Action Description Requires
fragments.list List all fragments
fragments.get Get fragment by ID id
fragments.create Create a new fragment payload (CreateFragmentDto)
fragments.update Update a fragment id, payload (UpdateFragmentDto)
fragments.delete Delete a fragment id
fragments.search Search by type/tag type and/or tag

Response Format

Success:

{ "ok": true, "data": { "id": "abc-123", "type": "!TRIGGER", "description": "...", "time": "...", "tags": [] } }

Error:

{ "ok": false, "error": "Description is required" }

Extending with New Modules

The Command class is generic — new modules use the same dot-notation pattern:

vault.unlock     → IVaultService (future)
vault.lock
entries.list     → IEntryService (future)
entries.create
ai.analyze       → IAiService (future)
ai.chat
search.query     → ISearchService (future)

To add a module:

  1. Create model, DTO, repository, and service in Journal.Core/
  2. Register the new service in ServiceCollectionExtensions.cs
  3. Inject the service into Entry.cs and add cases to the action switch
  4. No changes needed to Command.cs or App.cs

Dependency Injection

ServiceCollectionExtensions.cs wires everything up. Any host (sidecar, API, tests) calls:

services.AddFragmentServices();

This registers:

  • IFragmentRepositoryInMemoryFragmentRepository (singleton — one shared store)
  • IFragmentServiceFragmentService (transient — fresh instance per request)
Description
No description provided
Readme 14 MiB
Languages
C# 43.7%
Svelte 29.5%
Python 10.6%
TypeScript 10.4%
Rust 3.6%
Other 2%