Docs
Everything you need to write events and read them back. A log lives at https://<name>.dayfile.cloud.
The ideas
- Log
- Your one append-only record of everything that happened, at
NAME.dayfile.cloud. NAME is your username, so the username, the log and its address are the same thing. You get one log. Nothing in it is edited or deleted. - Event
- One entry in the log: a title, plus optional detail, tags and times. "Backup finished" is an event.
- Tag
- A free label you attach to group events, such as
launchorhealth. An event can have many tags, and you can filter by them. One tag is special:todo. An event taggedtodois a to-do: it starts open and gets a Close button. Every other event has no status. - Feed
- A saved filtered view of the log, for example "all events tagged
work". Feeds hold no data of their own: the log is the single source of truth and a feed is a window onto it. Your log has a default feed, and you can add named ones at/f/NAME. Reading a feed in a browser, an RSS or Atom feed reader or code gives the same events. - Live updates
- Staying connected so new events reach you as they arrive, instead of asking again (see Staying up to date). Any feed or the whole log can be followed this way.
- Token
- A password for one job. A write token can only add events, and can be limited to named sources. A read token can only read, limited by audience labels. Make one per script or device so you can revoke it alone.
- Audience
- A label on an event that decides who may read it. With none, only you can (
self). The labelpubliclets a public feed show it. - Source
- Who sent an event, for example
backup-script. It is how a write token is limited and how retries are recognized.
Quick start
Post an event
curl -X POST https://NAME.dayfile.cloud/events \
-H "Authorization: Bearer WRITE_TOKEN" \
-H "content-type: application/json" \
-d '{"title":"Hello"}'
It answers 201 with the stored event. Only title is required. dayfile fills in the id, the time, and the source when your write token has exactly one. Each quick post is a new event. To make retries safe, send the full form with a source.id and ref (see Events): posting the same pair again returns the existing event with 200.
Read it back
curl -H "Authorization: Bearer READ_TOKEN" https://NAME.dayfile.cloud/events
For an RSS reader (any feed reader that understands Atom or JSON Feed) that cannot send headers, use the token in the path:
https://NAME.dayfile.cloud/t/READ_TOKEN/feed.json
https://NAME.dayfile.cloud/t/READ_TOKEN/feed.atom
Anyone holding that URL can read the log. Treat it like a password.
Events
| Field | Meaning | |
|---|---|---|
specversion | required | "dayfile/1" |
id | required | A ULID you choose. Time-sortable, and can be made offline. |
source | required | {"id", "ref"}. id names the sender; ref is its own identifier, used to make posting idempotent. |
occurred_at | required | When it happened. UTC, YYYY-MM-DDTHH:MM:SSZ. |
title | required | Plain-text headline. |
body | Plain-text detail. | |
visible_at | When it appears in feeds. Defaults to occurred_at; a future time holds it back. | |
expires_at | When it stops appearing in feeds. | |
tags, urgency | Flat strings (fleet/anomaly is just a convention); urgency 0 to 3. The tag todo makes the event a to-do with a status. | |
payload | A JSON object of your own data. dayfile stores it and passes it along but does not check its shape; agree the shape with whatever reads it. | |
refs, links | [{rel, id}] and [{rel, href}]. | |
audience | Labels that decide who may read it. Missing means ["self"]. |
The server adds seq (the event's position in the log) and recorded_at. You can post one event or a JSON array of them; if any event is malformed, none is written.
Reading with filters
These work on /events, /feed.json and /feed.atom:
tag=a&tag=b | Events with all the tags (tag_any= for any). |
status=open | open (a todo not yet closed), closed, superseded, retracted or none (an ordinary event with no status). The default shows open, closed and none (not retracted or replaced). |
within=7d | Became visible in the last N minutes, hours or days (m, h, d). |
limit=N | Page size. Default 50, at most 200. |
since_seq=N | Only events after position N. Page with this, never with timestamps. |
GET /events/{id} returns one event. /events returns {"events": [...], "cursor": N, "released_cursor": T, "next": url or null}, oldest first.
Feeds, private and public
Your log has a default feed at /feed.json (or .atom) and you can add named feeds, each a saved filter:
https://NAME.dayfile.cloud/f/work.json
https://NAME.dayfile.cloud/f/work.atom
https://NAME.dayfile.cloud/t/READ_TOKEN/f/work.json (token in the path, for feed readers)
A feed without a suffix (/f/work) answers with JSON Feed. A named feed's filter is fixed: the URL accepts only paging (limit, since_seq).
A feed is private by default and needs a read token. Make it public and anyone can read it with no token, read only. A public feed shows only events that match its filter and carry the audience label public ("audience": ["public"]), so nothing is exposed unless you marked it. Posting still needs a write token. Turning a feed private stops it at once, though copies other people already made may remain.
Changing your mind
Events are never edited. A later event points back at the earlier one through refs:
completesmarks it closed. A closing event can leave outtitleand inherits the original's.supersedesreplaces it with a newer version.retractshides it. Retracting the retraction brings it back.replies-toonly threads.
A reference only takes effect when the event carrying it comes from the same source as its target, or the two share an audience label.
Staying up to date
- Long-poll:
GET /events?since_seq=N&wait=30waits up to 30 seconds for something new and returns an empty page if nothing comes. - Stream:
GET /events/streamsends Server-Sent Events and closes after 300 seconds, so reconnect withLast-Event-ID. A browser'sEventSourcecannot send anAuthorizationheader; usefetchwith a streaming body. - To catch events released late, keep
cursorandreleased_cursorfrom each response and send them back assince_seqandreleased_after.
Limits
- 600 events per minute per write token; 300 reads per minute per token.
- At most 8 open streams or long-polls per token.
- A request body is at most 1 MiB.
- Beyond a limit you get
429withRetry-After, and nothing is written.
Errors
401 | No token, or one that is unknown, expired or revoked. These look the same on purpose. |
403 | The token is real but not allowed to do that: a read token posting, a write token reading, a source it may not use, or a suspended log. |
404 | No such log or event. |
409 | The id is already used by a different event. |
422 | The event is not valid. The message says why. |
429 | Over a limit. Wait Retry-After seconds. |
Keeping tokens safe
Only a hash of each token is stored, so a lost token cannot be shown again; ask for a new one. The sign-in page on this site keeps your read token in a cookie that scripts cannot read and never prints it into a page.