Family organiser app for Eagraí Clainne (eagraiclainne.ie). https://eagraiclainne.ie
  • Kotlin 42.3%
  • Go 33.2%
  • TypeScript 20.7%
  • CSS 2.9%
  • Makefile 0.3%
  • Other 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Luke Mallon d709727a95
All checks were successful
check / commits (pull_request) Successful in 20s
check / go (pull_request) Successful in 2m54s
check / report (pull_request) Successful in 4s
android / build (pull_request) Successful in 7m59s
android / report (pull_request) Successful in 5s
check / web (pull_request) Successful in 4m30s
check / commits (push) Successful in 32s
check / go (push) Successful in 3m0s
check / report (push) Has been skipped
check / web (push) Successful in 6m54s
android / build (push) Successful in 7m29s
android / report (push) Has been skipped
tag / tag (push) Successful in 5m8s
release / docs (push) Successful in 1m19s
release / binaries (push) Successful in 1m59s
release / android (push) Successful in 4m59s
release / image (push) Successful in 3m57s
release / module (push) Successful in 28s
release / manifests (push) Successful in 29s
release / release (push) Successful in 15s
fix(android): confetti in its own window, over any open sheet
Three celebration defects on the phone (issue #283). A sub-job ticked
inside the job sheet handed the moment to its board, and the board drew
the confetti on its own canvas — under the sheet, which Material hosts
in a separate window. The calendar page opened the job sheet with no
celebrate hook at all, so a points tick from a calendar row's sheet
never burst. And the wide calendar (week, month, the day sheet) set
its confetti origin but only the phone branch composed the overlay, so
those bursts never drew either.

ConfettiOverlay now composes into a transparent, touch-through window
of its own (a Compose Dialog with the dim, focus and touch flags
cleared), so it sits above whichever sheet or dialog fired it — the
web's fixed-position canvas, which the DOM stacks over the sheet for
free. Origins move to screen coordinates so a burst fired from a
sheet's window lands where the tap was. The calendar page wires
onCelebrate like the Jobs and Today boards, and hosts one overlay for
every view.

Verified on the emulator against the e2e server: ticking a points
sub-job inside the job sheet raises a third app window on top with
NOT_FOCUSABLE NOT_TOUCHABLE and no dim, above the sheet's window, and
it retires when the burst ends. Two emulator gotchas met on the way:
the seed's admin has reduce_motion on, and an AVD with
animator_duration_scale 0 reads as system reduce-motion — both skip
the confetti by design.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 17:31:31 +01:00
.dagger feat!: rename famorgasy to Eagraí Clainne 2026-08-11 15:47:15 +01:00
.gitea/workflows chore(deploy): a one-tap Obtainium add link in the release notes 2026-08-30 19:09:27 +01:00
.github feat!: rename FAM_ env prefix to EAG_ 2026-08-11 16:23:19 +01:00
android fix(android): confetti in its own window, over any open sheet 2026-09-12 17:31:31 +01:00
cmd feat(tokens): the clash neutral leaves the palette 2026-08-29 11:29:35 +01:00
conformance feat(android): the happening-today timeline on the home board 2026-08-30 16:55:49 +01:00
deploy chore(deploy): a one-tap Obtainium add link in the release notes 2026-08-30 19:09:27 +01:00
design feat(tokens): the clash neutral leaves the palette 2026-08-29 11:29:35 +01:00
docs docs(design): drop the home rail and timeline handoff bundle 2026-08-30 15:50:16 +01:00
docsite feat!: rename famorgasy to Eagraí Clainne 2026-08-11 15:47:15 +01:00
gen feat(proto): the calendar view pref learns week 2026-08-26 08:01:47 +01:00
internal fix(item): keep list entries out of the notification stream 2026-09-12 17:31:31 +01:00
pkg feat(event): join or leave one occurrence 2026-08-26 08:01:46 +01:00
proto/api/core/v1 feat(proto): the calendar view pref learns week 2026-08-26 08:01:47 +01:00
scripts fix(android): freeze the device clock to the e2e anchor 2026-08-19 14:43:46 +01:00
tools feat!: rename famorgasy to Eagraí Clainne 2026-08-11 15:47:15 +01:00
web feat(android): the happening-today timeline on the home board 2026-08-30 16:55:49 +01:00
.dockerignore fix(build): make the image build hermetic and drop dead catalogs 2026-08-13 13:06:47 +01:00
.env.example feat!: rename FAM_ env prefix to EAG_ 2026-08-11 16:23:19 +01:00
.gitignore test(android): maestro page flows against the e2e server 2026-08-19 14:09:49 +01:00
AGENTS.md feat(jobs): the job options sheet and the repeat sheet 2026-08-26 08:01:47 +01:00
buf.gen.android.yaml feat!: rename famorgasy to Eagraí Clainne 2026-08-11 15:47:15 +01:00
buf.gen.ts.yaml fix(build): make the image build hermetic and drop dead catalogs 2026-08-13 13:06:47 +01:00
buf.gen.yaml feat!: rename famorgasy to Eagraí Clainne 2026-08-11 15:47:15 +01:00
buf.yaml feat!: rename famorgasy to Eagraí Clainne 2026-08-11 15:47:15 +01:00
CLAUDE.md docs(fix): Reduce duplication and maintanence burdon by symlinking CLAUDE.md 2026-08-14 09:43:20 +01:00
CONTRIBUTING.md docs: add contributing guide and enforce conventional commits 2026-08-13 21:17:13 +01:00
cosign.pub fix: update the cosign public key 2026-08-11 19:47:28 +01:00
dagger.json feat!: rename famorgasy to Eagraí Clainne 2026-08-11 15:47:15 +01:00
devbox.json chore: everything states its version 2026-08-07 19:29:41 +01:00
devbox.lock chore: refresh devbox.lock after a devbox update 2026-08-07 19:29:41 +01:00
Dockerfile test(web): pin the shared label fixtures with vitest 2026-08-19 10:25:42 +01:00
go.mod fix(user): lock out repeated failed password logins 2026-08-13 12:30:25 +01:00
go.sum feat(docs): generated docs site via cmd/docsgen 2026-08-08 21:53:18 +01:00
LICENSE chore: add gitignore, license and readme 2026-08-07 19:19:27 +01:00
LOCAL_DEVELOPMENT.md feat!: rename FAM_ env prefix to EAG_ 2026-08-11 16:23:19 +01:00
Makefile feat(jobs): the job options sheet and the repeat sheet 2026-08-26 08:01:47 +01:00
README.md docs(readme): add CI, release and license badges 2026-08-19 21:42:17 +01:00

Eagraí Clainne

CI Android Latest release License: AGPL-3.0 Donate on Liberapay

Family Organization System - A gRPC-based application for organizing family activities, events, items, and rewards.

Overview

Eagraí Clainne is a Go-based backend service that provides APIs for managing family organization tasks including:

  • User Management: Create and manage family member accounts with authentication
  • Events: Schedule and coordinate family events with participant tracking
  • Items: Track family items and assign them to users with deadlines
  • Rewards: Create reward systems and allow users to claim rewards

It serves a bundled web frontend and a native Android client, and also speaks the Model Context Protocol for assistant clients.

The service is built using:

  • Connect RPC (gRPC-compatible protocol)
  • Protocol Buffers for API definitions
  • SQLite by default, PostgreSQL optional for data persistence, selected by EAG_DB_DRIVER (see ADR-0021)
  • JWT authentication with role-based access control (RBAC)

Client Library

A Go client library is available in pkg/client/ for building CLI tools and applications:

import "eagraiclainne.ie/pkg/client"

c, _ := client.New("http://localhost:8080", nil)
loginResp, _ := c.Users.Login(ctx, "user@example.com", "password")
c.SetAuthToken(loginResp.Token)
event, _ := c.Events.Create(ctx, &client.CreateEventParams{...})

See pkg/client/README.md for full documentation and examples.

Architecture

The project follows a clean architecture with clear separation of concerns:

cmd/server/          - Server entry point and configuration
internal/
  auth/              - Authentication, JWT handling, RBAC, and interceptors
  database/          - Database abstraction layer with generic table operations
  services/          - Business logic for each domain (user, event, item, reward)
proto/api/core/v1/   - Protocol Buffer API definitions
gen/go/api/core/v1/  - Generated Go code from Protocol Buffers

Development Setup

📖 For detailed local development instructions, see LOCAL_DEVELOPMENT.md

Quick Start

Local development runs on a kind cluster (see deploy/README.md):

# One-time: bootstrap the kind cluster + local registry
make cluster-up

# Build the image and deploy database, observability and app
make deploy

# Fresh database only: seed a family to log in with
make seed-family

The app is then available at http://localhost:8080.

Before committing, run:

make check   # fmt, vet, lint, test, and web/android i18n checks

Alternative: Using Devbox

This project also supports devbox for reproducible development environments:

# Install devbox
curl -fsSL https://get.jetpack.io/devbox | bash

# Enter devbox shell
devbox shell

# Follow the Quick Start steps above

Development Commands

Building

go build ./...                                    # Build all packages
go build -o ./bin/server ./cmd/server            # Build server executable

Testing

go test ./...                                     # Run all tests
go test -v ./internal/database/ -run TestName    # Run specific test

Code Quality

go fmt ./...           # Format code
go vet ./...           # Vet code for errors
golangci-lint run      # Run linter

Protocol Buffers

buf generate           # Generate Go code from .proto files
buf lint               # Lint protocol buffer definitions

Environment Variables

Configuration is read via viper with the EAG_ prefix, so a db-driver flag maps to the EAG_DB_DRIVER environment variable. The database ones:

Variable Default Description
EAG_DB_DRIVER sqlite Storage engine (sqlite or postgres)
EAG_DB_PATH eagraiclainne.db SQLite database file (driver sqlite)
EAG_DB_HOST localhost Database host (driver postgres)
EAG_DB_PORT 5432 Database port (driver postgres)
EAG_DB_USER postgres Database user (driver postgres)
EAG_DB_PASSWORD (empty) Database password (driver postgres)
EAG_DB_NAME eagraiclainne Database name (driver postgres)
EAG_DB_SSLMODE disable SSL mode (driver postgres)

API Services

The following Connect RPC services are available:

  • UserService: User registration, login, profile management
  • EventService: Event creation, updates, user participation
  • ItemService: Item tracking, user assignment, deadline management
  • ItemListService: Grouping of items into lists
  • RewardService: Reward creation, claiming, user reward tracking
  • MealService / MealRotaService: Meal planning and the cooking rota
  • ApiKeyService: Admin-managed machine credentials (API keys)
  • SystemService: Data export, import and restore
  • WebhookService: Outbound signed event deliveries

MCP Endpoint

The server speaks the Model Context Protocol at /mcp (streamable HTTP), so Claude and other MCP clients can work the family data with the tools list_items, add_item, complete_item, list_events and add_event. Authenticate with an API key (mint one in the web Admin → Keys panel, or with eagraiclainne apikey create):

claude mcp add eagraiclainne --transport http http://localhost:8080/mcp \
  --header "Authorization: Bearer <api-key-token>"

A personal key acts as one member. A standalone key (for a shared device such as Home Assistant) names who actually acted per call via acting_user, honoured only against its admin-set allowlist. See ADR-0016.

Authentication & Authorization

The service implements JWT-based authentication with RBAC:

  • Users authenticate via the Login RPC
  • JWT tokens are required for most operations (via Authorization header)
  • Roles determine access permissions (Admin, Member, Child, Guest)
  • Admin-managed API keys serve machine clients; the key record is the authority (per-key revocation, acting-user allowlists) — see ADR-0016
  • See ADR-0007, ADR-0008, and ADR-0009 for details

ADR Process

For significant changes, we record Architecture Decision Records (ADRs). ADRs document design decisions and their rationale for:

  • New authentication/authorization systems
  • Database schema changes
  • New API services or significant endpoint additions
  • Major refactoring affecting multiple packages
  • Changes to core architecture or design patterns

ADRs are stored in docs/adr/<nnnn>-<topic>.md and should include sections for context, decision, consequences, and alternatives considered. Existing ADRs are in docs/adr/.

Database Schema

The database schema is defined in internal/database/schema.sql (PostgreSQL) and internal/database/schema_sqlite.sql (SQLite), applied on boot, and includes tables for:

  • Users and authentication
  • Events and event participants
  • Items and item assignments
  • Rewards and reward claims

The database layer provides generic, type-safe operations using Go generics.

Contributing

See CONTRIBUTING.md for how to get access, build, commit, and submit a change. In short: follow the project conventions, write tests for new functionality, record an ADR for significant architectural changes, and make sure make check passes before submitting.

License

See LICENSE file for details.