Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Daybreak environment variables
#
# Copy this file to `.env` and fill in real values:
# cp .env.example .env
#
# `.env` is gitignored. Never commit real secrets.

# --- Required ---------------------------------------------------------------

# Rails master key,decrypts config/credentials.yml.enc.
# If you don't have one yet, generate it after `git clone` with:
# bin/rails credentials:edit
# which will create config/master.key. Copy that value here.
RAILS_MASTER_KEY=

# Basecamp OAuth credentials.
# Create a Basecamp integration at https://launchpad.37signals.com/integrations
# and set the redirect URL to: http://localhost:3000/auth/basecamp/callback
# (replace the host when deploying to a real domain).
BASECAMP_CLIENT_ID=
BASECAMP_CLIENT_SECRET=

# --- Optional ---------------------------------------------------------------

# Embed Solid Queue inside the Puma process (single-container self-host).
# Set to `false` only if you're running `bin/jobs` in a separate process.
SOLID_QUEUE_IN_PUMA=true

# Server tuning,defaults below match the production Dockerfile.
# PORT=3000
# RAILS_MAX_THREADS=3
# WEB_CONCURRENCY=1
# RAILS_LOG_LEVEL=info

# --- HEY integration (optional) --------------------------------------------
# HEY uses a public PKCE client baked into the app,no secret needed.
# Connect from the app UI at /auth/hey after logging in with Basecamp.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [is2b007]
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
# Ignore bundler config.
/.bundle

# Ignore all environment files.
# Ignore all environment files (but keep the template).
/.env*
!/.env.example

# Ignore all logfiles and tempfiles.
/log/*
Expand All @@ -32,6 +33,9 @@

# Ignore key files for decrypting credentials and more.
/config/*.key
# Defensive: only the encrypted `.enc` variant should ever be committed.
/config/credentials.yml
/config/credentials/*.yml

# Claude Code local settings and worktree checkouts
/.claude/
Expand Down
70 changes: 70 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Contributing to Daybreak

Thanks for your interest in Daybreak. This document covers local setup, conventions, and how to propose changes.

## Philosophy

Daybreak is intentionally small and opinionated. It follows the 37signals stack and product philosophy: software should have opinions, not options.

Before proposing a feature, check whether it fits. Changes that add team features, analytics, AI/LLM integrations, notifications, or a fourth view are likely out of scope. When in doubt, open an issue first and we'll chat.

## Tech stack (non-negotiable)

- Ruby on Rails 8.1
- SQLite
- Hotwire (Turbo + Stimulus)
- Propshaft + Import Maps
- Solid Queue / Solid Cache / Solid Cable
- Minitest + Capybara

No React. No Tailwind. No TypeScript. No bundler. No CSS framework. If a new dependency isn't already used by vanilla Rails 8, it probably doesn't belong here.

## Local setup

```bash
git clone https://github.com/is2b007/daybreak.git
cd daybreak
bin/setup
```

`bin/setup` will install gems, prepare the database, and print next steps.

You'll need a Basecamp OAuth app to sign in. Create one at [launchpad.37signals.com/integrations](https://launchpad.37signals.com/integrations) and set the redirect URL to `http://localhost:3000/auth/basecamp/callback`. Then copy `.env.example` to `.env` and fill in the credentials.

Start the dev server:

```bash
bin/dev
```

Open [http://localhost:3000](http://localhost:3000).

## Running tests

```bash
bin/rails test # unit + integration
bin/rails test:system # headless browser
```

System tests require a Chrome/Chromium install.

## Submitting changes

1. Fork, branch, commit.
2. Keep PRs small and focused. One change per PR.
3. Include tests for behavior changes.
4. Run `bin/rails test` before pushing.
5. Open a PR against `main`. Describe the user-visible change and link any related issue.

For larger changes, open an issue first to discuss scope.

## Reporting bugs

Open an issue with:

- What you did
- What you expected
- What happened instead
- Rails log excerpt if relevant

For security issues, **do not file a public issue**, see [SECURITY.md](SECURITY.md).
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Kosta Canatselis

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
149 changes: 86 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,113 +1,136 @@
# Daybreak

Daybreak is a personal planning layer between Basecamp and HEY.
A calm daily and weekly planner that bridges [Basecamp](https://basecamp.com) and [HEY](https://hey.com) into one opinionated workspace.

It pulls your assignments, schedules, and HEY inputs into one calm workspace so you can shape a realistic day, run a morning/evening ritual, and keep momentum without creating another task silo.
**Website:** [daybreakplanner.com](https://daybreakplanner.com)

## What Daybreak does
Daybreak pulls your Basecamp assignments and schedules, optionally layers HEY calendar events and email triage on top, and gives you a week view, a day view, and a focus mode for working on a single task. That's it. No team features, no AI, no notifications, no analytics.

- Signs in with your Basecamp account (OAuth) and syncs assignments/schedules.
- Optionally connects HEY for calendar events, journal sync, and email triage.
- Gives you week and day planning views with drag/drop task flow.
- Includes daily rituals, timeboxing, and a lightweight focus timer.
- Supports personal local tasks alongside synced tasks.
Built on the 37signals stack (Rails 8, Hotwire, SQLite). Designed for self-hosting.

## Tech stack
## What Daybreak does

- Ruby on Rails `8.1`
- SQLite (default)
- Hotwire (`turbo-rails`, `stimulus-rails`)
- Importmap + Propshaft
- Solid Queue / Solid Cache / Solid Cable
- Minitest + Capybara + Selenium
- **Week view:** kanban of days across the week, plus a "sometime" bucket. Drag tasks between days.
- **Day view:** today's tasks with timeboxing, calendar events pinned from HEY, and a daily log.
- **Focus mode:** single-task view with a lightweight timer for deep work.
- **Morning ritual:** review yesterday, plan today, add events to the week.
- **Evening ritual:** close out open items, reflect, log the day.
- **Basecamp sync:** OAuth sign-in; auto-syncs your assignments and schedules; triage your Basecamp inbox into day or week.
- **HEY integration (optional):** calendar events, email triage (Imbox / Reply Later / Set Aside), journal digest, email-to-task.

## Prerequisites
## Self-host it

- Ruby (matching `.ruby-version` if present, or current Rails 8-compatible Ruby)
- Bundler
- SQLite 3
Daybreak is single-user and made to run on your own machine or small server. The easiest path is Docker Compose.

## Getting started
### Quick start

```bash
git clone https://github.com/is2b007/daybreak.git
cd daybreak
bundle install
bin/rails db:prepare
bin/rails server
cp .env.example .env # fill in BASECAMP_CLIENT_ID / SECRET / RAILS_MASTER_KEY
docker compose up -d
open http://localhost:3000
```

Then open [http://localhost:3000](http://localhost:3000).
### Deploy to Railway (1-click)

## Configuration
[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy?template=https://github.com/is2b007/daybreak)

### Basecamp OAuth (required for sign-in)
Before you click: Daybreak needs its own Basecamp OAuth app. There's no shared
public client, each self-hoster creates their own.

Daybreak needs Basecamp OAuth credentials. You can provide them either via encrypted Rails credentials or environment variables.
1. Create a Basecamp integration at
[launchpad.37signals.com/integrations](https://launchpad.37signals.com/integrations).
Use a placeholder redirect URL for now (e.g. `https://example.com/auth/basecamp/callback`).
Copy the Client ID and Client Secret.
2. Click the Deploy on Railway button above. Railway builds the Dockerfile and
prompts for the three required env vars:
- `RAILS_MASTER_KEY` (generate locally with `bin/rails credentials:edit`, copy `config/master.key`)
- `BASECAMP_CLIENT_ID`
- `BASECAMP_CLIENT_SECRET`
3. After the build finishes, Railway assigns a domain
(e.g. `daybreak-production.up.railway.app`). Go back to your Basecamp
integration and update the redirect URL to
`https://<your-railway-domain>/auth/basecamp/callback`.
4. Add a persistent volume so your data survives restarts:
service → **Settings → Volumes** → **New Volume**, mount path `/rails/storage`.
Restart the service.

Environment variable fallback:
Expect roughly $5/mo on Railway's Hobby plan for a single-user deploy.

```bash
export BASECAMP_CLIENT_ID="..."
export BASECAMP_CLIENT_SECRET="..."
```
### Get Basecamp credentials

Credentials alternative (`bin/rails credentials:edit`):
1. Go to [launchpad.37signals.com/integrations](https://launchpad.37signals.com/integrations) and create a new integration.
2. Set the redirect URL to `http://localhost:3000/auth/basecamp/callback` (or your real host when deploying).
3. Copy the Client ID and Client Secret into your `.env` file as `BASECAMP_CLIENT_ID` and `BASECAMP_CLIENT_SECRET`.

```yml
basecamp:
client_id: ...
client_secret: ...
### Generate a Rails master key

If you're starting fresh and don't have a `config/master.key`:

```bash
bin/rails credentials:edit
```

### HEY integration (optional)
That creates `config/master.key`. Copy the contents into `.env` as `RAILS_MASTER_KEY`.

HEY uses PKCE with Daybreak's built-in public client and can be connected from the app UI (`/auth/hey`).
### Data persistence

When connected, Daybreak can sync:
The `docker-compose.yml` mounts `./storage` from the host into the container, so your SQLite databases and Active Storage blobs survive restarts. Back up this folder and you've backed up everything.

- Calendar events
- HEY journal updates
- HEY email triage (Imbox, Reply Later, Set Aside)
## Develop it

## Running tests
If you want to hack on Daybreak instead of just run it:

```bash
bin/rails test
git clone https://github.com/is2b007/daybreak.git
cd daybreak
bin/setup
bin/dev
```

## Background jobs
See [CONTRIBUTING.md](CONTRIBUTING.md) for conventions, stack constraints, and how to submit changes.

## Stack

- Ruby on Rails 8.1
- SQLite (Solid Queue / Solid Cache / Solid Cable all on SQLite too)
- Hotwire (Turbo + Stimulus)
- Propshaft + Import Maps, no bundler
- Minitest + Capybara
- Kamal for deployment (optional)

No React, Tailwind, TypeScript, or CSS framework. By design.

Daybreak uses Solid Queue. In development, jobs can run in Puma with `SOLID_QUEUE_IN_PUMA=true` or via:
## Tests

```bash
bin/jobs
bin/rails test # unit + integration
bin/rails test:system # headless browser
```

## Deployment
## Deploying to a real server

The project includes Kamal deployment config in `config/deploy.yml` (template values should be updated for your server/domain).

Typical deploy flow:
A Kamal config lives in `config/deploy.yml` with placeholder IP and domain values. Fill those in with your own server and domain, then:

```bash
bin/kamal setup
bin/kamal deploy
```

## Project structure

- `app/` - Rails MVC app code, jobs, and services
- `specs/` - implementation specs and scoped product work docs
- `TODOS.md` - follow-up engineering/security tasks
- `CHANGELOG.md` - release history
Kamal expects Docker on the target host and uses the same Dockerfile as the local compose setup.

## Security and operational notes
## Project layout

- Never commit plaintext secrets.
- Keep `config/credentials.yml.enc` and your `RAILS_MASTER_KEY` secure.
- Review `TODOS.md` for known deferred hardening items.
- `app/` : Rails MVC code, jobs, services
- `site/` : static marketing site served at [daybreakplanner.com](https://daybreakplanner.com)
- `specs/` : implementation specs and product docs
- `CHANGELOG.md` : release history

## License

Proprietary (unless you choose to add an OSS license).
[MIT](LICENSE). Do what you want; attribution appreciated.

## Support the work

If Daybreak is useful to you, [sponsor the project on GitHub](https://github.com/sponsors/is2b007). Sponsorships keep the site online and pay for the time to maintain it.
33 changes: 33 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Security Policy

## Reporting a vulnerability

If you discover a security issue in Daybreak, please report it privately, do **not** file a public GitHub issue.

Email: **kostac@hey.com**

Please include:

- A description of the issue and its impact.
- Steps to reproduce (or a proof-of-concept).
- The version / commit hash you tested against.

I'll acknowledge receipt within a few days and work with you on a fix and disclosure timeline.

## Scope

Daybreak is a single-user self-hosted app. The most relevant concerns are:

- OAuth token handling for Basecamp and HEY.
- Rails credentials / master key storage.
- Any path that handles user-supplied content from Basecamp or HEY payloads.

## What's out of scope

- Vulnerabilities that require a compromised host machine or valid Basecamp admin credentials.
- Missing rate limiting on a single-user app.
- CSRF/XSS reports generated by automated scanners without a demonstrable impact.

## Thanks

Security research is appreciated. Responsible disclosure helps everyone who self-hosts Daybreak.
Loading