Choosing the Tech Stack
With the architecture defined, the technology choices followed naturally. The stack reflects a pragmatic balance between TypeScript for the browser ecosystem and Go for performance-critical components.
TypeScript: The Client SDK
The TypeScript SDK targets browsers only. It provides:
- Core: Document model, merge engine, encryption, sync protocol
- Storage: Browser adapters (IndexedDB via wa-sqlite or idb)
- Sync Adapters: Google Drive, OneDrive, Custom API, WebRTC (future)
Go: The Backend and Client SDK
Go handles two roles:
- Custom Sync Server: REST + SSE API, SQLite metadata, flat file storage
- Client SDK: Same capabilities as the TypeScript version, but for Go applications
Clean Architecture: Consistent Across Languages
Both TypeScript and Go follow Clean Architecture principles:
- Entities: Core domain objects
- Use Cases: Business logic with interface-based DI
- Partial Entities: DTOs that wrap entities for UC consumption (e.g.,
PartialDocument) - Handlers: API layer with Payloads
- Persistence: Storage implementations with sqlc repositories
- Gateways: External service adapters (cloud storage, APIs)
API Definition: TypeSpec + ogen
The custom API is defined using TypeSpec and code is generated with ogen. This provides:
- Clean, type-safe Go code
- No runtime dependencies
- Payloads mapped to Use Case inputs
Database: sqlc + goose
For the Go server:
- sqlc: Type-safe SQL queries generated from SQL definitions
- goose: Database migrations (embedded via
go:embed)
Monorepo: pnpm Workspaces
The TypeScript packages are organized as a pnpm monorepo with a Taskfile for unified task running.
The technology stack demonstrates a thoughtful approach to polyglot development. TypeScript dominates the browser ecosystem, while Go excels at backend services and CLI tools. The shared Clean Architecture principles ensure consistency across languages, making the codebase maintainable despite the dual-language approach.





