Keystone SystemsKS Systems

Personal Information Management

Keystone provides a suite of CLI tools from the Pimalaya project for managing email, calendars, contacts, and timers. All tools share the same credential pattern — when mail is configured, calendar and contacts auto-default from it. No extra secrets needed.

Tools Overview

ToolPurposeBackendModule
HimalayaEmailIMAP/SMTP (Stalwart)keystone.terminal.mail
CalendulaCalendarsCalDAV (Stalwart)keystone.terminal.calendar
CardamumContactsCardDAV (Stalwart)keystone.terminal.contacts
ComodoroTimersLocal (Unix socket)keystone.terminal.timer

Configuration

Enable Everything

If mail is already configured, enabling the other tools requires one line each:

keystone.terminal.mail = {
  enable = true;
  accountName = "personal";
  email = "me@example.com";
  displayName = "My Name";
  login = "me";
  host = "mail.example.com";
  passwordCommand = "cat /run/agenix/mail-password";
};

keystone.terminal.calendar.enable = true;
keystone.terminal.contacts.enable = true;
keystone.terminal.timer.enable = true;

Calendar and contacts inherit accountName, host, login, and passwordCommand from the mail module. Override any option explicitly if needed.

Generated Config Files

Each tool gets a TOML config in ~/.config/:

ToolConfig Path
Himalaya~/.config/himalaya/config.toml
Calendula~/.config/calendula/config.toml
Cardamum~/.config/cardamum/config.toml
Comodoro~/.config/comodoro/config.toml

These are managed by Home Manager — don't edit them manually.

Email (Himalaya)

List Inbox

himalaya envelope list
himalaya envelope list -f "Sent Items"

Read a Message

himalaya message read <id>

Send an Email

Always include a Date: header. Without it, emails show as 1970-01-01.

echo "From: me@example.com
To: recipient@example.com
Subject: Hello
Date: $(date -R)
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8

Message body here" | himalaya message send

Folder Names

Stalwart uses different folder names than Himalaya defaults. The module auto-maps them:

Himalaya DefaultStalwart Name
SentSent Items
DraftsDrafts
TrashDeleted Items

Calendars (Calendula)

Calendula connects to Stalwart's CalDAV endpoint at /dav/cal.

List Calendars

calendula calendars list

List Events

calendula items list <calendar-id>

Create or edit an event

Calendula v0.1.0 edits raw iCalendar data through your $EDITOR. In practice, that means event creation and updates are done by writing a VEVENT with the fields you want to set.

Minimum useful fields:

  • SUMMARY for the event title
  • DTSTART for the start date and time
  • DTEND for the end date and time, or a reasonable assumed duration
  • LOCATION for the venue or address
  • DESCRIPTION for contact details, notes, room info, or agenda

Common examples:

BEGIN:VEVENT
UID:intuitive-machines-presentation@example.com
DTSTAMP:20260331T120000Z
DTSTART;TZID=America/Chicago:20260417T090000
DTEND;TZID=America/Chicago:20260417T100000
SUMMARY:Intuitive Machines Company Presentation
LOCATION:13467 Columbia Shuttle Street\, Houston\, TX 77059
DESCRIPTION:Contact\nIntuitive Machines\n13467 Columbia Shuttle Street\, Houston\, TX 77059\n281.520.3703
END:VEVENT

Add alerts with VALARM blocks:

BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Intuitive Machines Company Presentation
TRIGGER:-P1D
END:VALARM
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Intuitive Machines Company Presentation
TRIGGER:-PT2H
END:VALARM

The example above sets reminders for one day before and two hours before the event start time.

Create an event

calendula items create <calendar-id>

This opens a new iCalendar draft in your $EDITOR.

Update an Event

calendula items update <calendar-id> <item-id>

This opens the iCalendar (.ics) data in your $EDITOR.

Practical event workflow

# 1. Find the calendar
calendula calendars list

# 2. Create a new event in that calendar
calendula items create default

# 3. List items to find the event ID later
calendula items list default

# 4. Update the event to change the title, date, time, location, or alerts
calendula items update default <item-id>

When editing the iCalendar body:

  • Change SUMMARY to rename the event
  • Change DTSTART and DTEND to move the date or time
  • Set LOCATION to the venue or street address
  • Put phone numbers, contact names, and notes in DESCRIPTION
  • Add VALARM blocks for reminders

Contacts (Cardamum)

Cardamum connects to Stalwart's CardDAV endpoint at /dav/card.

List Address Books

cardamum addressbooks list

List Contacts

cardamum cards list <addressbook-id>

Update a Contact

cardamum card update <addressbook-id> <card-id>

This opens the vCard (.vcf) data in your $EDITOR.

Timers (Comodoro)

Comodoro is a centralized Pomodoro timer with a client-server architecture. Multiple clients can control the same timer session.

Default Cycles

The keystone module configures standard Pomodoro cycles:

CycleDuration
Work25 min
Rest5 min
Work25 min
Rest5 min
Work25 min
Long rest30 min

Start the Server

comodoro server start

Control the Timer

comodoro timer start    # Start the first cycle
comodoro timer get      # Show current timer state
comodoro timer pause    # Pause
comodoro timer resume   # Resume

Notifications

The module enables desktop notifications by default:

  • Work started — "Work started!"
  • Rest started — "Take a break!"
  • Long rest started — "Long break time!"

OS Agents

Agents provisioned via keystone.os.agents automatically receive email, calendar, and contacts when mail is enabled. No extra configuration is needed — credentials are derived from the agent's mail account.

ToolAgent Config
Himalayakeystone.terminal.mail.enable = true (auto-configured)
Calendulakeystone.terminal.calendar.enable = true (auto-configured)
Cardamumkeystone.terminal.contacts.enable = true (auto-configured)

Verify an agent's access:

agentctl drago exec himalaya envelope list
agentctl drago exec calendula calendars list
agentctl drago exec cardamum addressbooks list

Stalwart DAV Endpoints

All CalDAV/CardDAV access goes through Stalwart's built-in DAV support:

ProtocolWell-KnownDirect URI
CalDAV/.well-known/caldav/dav/cal
CardDAV/.well-known/carddav/dav/card

The modules use direct URIs (home-uri) rather than discovery because Stalwart's well-known redirects trigger a nginx 400 on the follow-up PROPFIND.

Debugging

Authentication Failures

The login field is the Stalwart account name (e.g., ncrmro), not the email address. Using the email as login causes auth failures for all tools.

Connection Issues

# Test CalDAV directly
curl -X PROPFIND -u "username:password" -H "Depth: 0" \
  https://mail.example.com/dav/cal

# Test CardDAV directly
curl -X PROPFIND -u "username:password" -H "Depth: 0" \
  https://mail.example.com/dav/card

Debug Logging

All Pimalaya tools support --debug for verbose output:

himalaya --debug envelope list
calendula --debug calendars list
cardamum --debug addressbooks list