Skip to content

Releases: besoeasy/originless

1.0.7

Choose a tag to compare

@github-actions github-actions released this 07 Oct 05:06

Full Changelog: 1.0.5...1.0.7

1.0.5

Choose a tag to compare

@github-actions github-actions released this 04 Oct 12:20

Full Changelog: 1.0.3...1.0.5

1.0.3

Choose a tag to compare

@github-actions github-actions released this 29 Sep 11:06

Full Changelog: 1.0.1...1.0.3

1.0.1

Choose a tag to compare

@github-actions github-actions released this 28 Sep 03:18

Patch release. Fixes six correctness bugs in the event store, adds a /cid/{cid} introspection route, restructures the Go source into cmd/ and internal/, and bounds upload and event resource usage.

Docker:

docker run -d --name originless -p 3232:3232 ghcr.io/besoeasy/originless:1.0.1

Multi-arch (linux/amd64, linux/arm64) and cosign-signed, tagged :latest and :1.0.1.

Breaking changes

Event owner is normalized to lower case before it is signed and stored. One public key now always produces the same event ID, counts once in unique_owners, and matches an ?owner= filter regardless of the case used to query it. Previously a client signing an upper-case hex key got a different event ID for the same logical event, and its events were invisible to lower-case owner queries.

Clients must sign the lower-case form of their key. A client that signs an upper-case form now receives 401 bad sig.

include_expired is removed. Expired events are never served, so the parameter had no meaning. GET /events/{id} returns 404 for an expired event, and GET /events omits it. See the expiry fix below for why the parameter was unreliable in the first place.

Fixed

Expired events were destroyed by ordinary reads. Any GET /events or GET /events/{id} deleted expired events from the store as a side effect. That made include_expired=true return 404 for events that had not actually expired away, and pinned /stats.expired at 0 after the first query. Reads now only filter; deletion belongs to a reaper that runs hourly, so expired in /stats means "past TTL, not yet reclaimed". This also fixes the opposite failure: with no reaper, an idle node previously accumulated expired events indefinitely.

/events/stream could not resume. The stream ignored Last-Event-ID, so every event published while a client was disconnected was lost to it until a full page reload. The stream now sends a retry: 3000 field and, on reconnect, replays everything published after the anchor event. The subscriber attaches before the backlog snapshot, so nothing slips through the reconnect gap, and replayed IDs are deduplicated. Replay is capped at the newest 100 events and is skipped if the anchor is no longer held.

The shared canvas lost pixels on startup. The dashboard subscribed to the event stream only after loading history, permanently dropping any pixel published in between. It now subscribes first and buffers, and merges buffered events with fetched history in publish order — sorting them together matters, because a buffered event older than a history event for the same cell would otherwise regress that cell.

next_cursor was returned on the final page. The cursor was emitted whenever a page exactly filled the limit, which cannot distinguish "exactly full" from "more available". Clients always made one extra request to discover the end. The cursor is now only returned when a further page exists.

/healthz reported a healthy container with a dead IPFS node. It never contacted the node, so Docker's HEALTHCHECK passed while uploads, downloads and stats all failed. It now verifies the node and returns 503 {"status":"degraded","ipfs":"unavailable"} when unreachable.

docs/api.md documented 502 for an unreachable node where the code and its test have always returned 503.

Added

GET /cid/{cid} — everything the local node reports about a CID as JSON: raw block stat, UnixFS object stat, directory links, base64 content for single blocks up to 1 MiB, and the content decoded when it is valid JSON. Reports available: false rather than fetching from the public network, and returns 503 when the node itself is unreachable.

Resource bounds. All optional, all with working defaults:

Variable Default Description
MAX_EVENTS 10000 In-memory event cap. The oldest live events are evicted once reached. Same class of bound as the existing per-event size, TTL, page and subscriber limits.
UPLOAD_TMPDIR system temp Directory multipart uploads are spooled to. Free space is probed with statfs and a full volume returns 507 before the request body is read.
PORT 3232 HTTP port inside the container.
IPFS_API_URL http://127.0.0.1:5001 Kubo RPC endpoint.

/ipfs/{cid} sets a 30-second per-write deadline, refreshed on every chunk. A client that stops reading now releases its goroutine and its IPFS connection instead of holding them open indefinitely, while a slow-but-progressing transfer is never cut off mid-download.

podman.sh forwards the documented settings to the container, including STORAGE_MAX, which it previously dropped, and prints the detected LAN address so other devices can open the dashboard.

Changed

Go source restructured into cmd/originless and internal/{server,events,ipfs,testutil}. The API is unchanged.

Docker and CI take the Go version from go.mod via a GO_VERSION build arg and go-version-file, instead of hardcoding 1.26 against a module declaring go 1.24.0. The builder, the test run and the module can no longer disagree.

README rewritten for Docker-first usage, with the full API reference moved to docs/api.md.

No access control, by design

There are none. Originless has no user accounts, no passwords and no API token. An Ed25519 signature on an event is the only credential in the protocol, and it proves authorship — who wrote an event — not permission to write it. Any client that can reach the port can publish to any collection, and CORS is * because there are no credentials for a browser to withhold.

The network boundary is the trust boundary. The container publishes 3232 on every interface so devices on your LAN can open the shared canvas and chat. To keep a run private:

docker run -d --name originless -p 127.0.0.1:3232:3232 ghcr.io/besoeasy/originless:1.0.1

For multi-user applications, enforce authorization in your own client or in a reverse proxy. The settings above are resource bounds, not access control.

Known limitations

  • Events are held in memory and are lost when the container stops. Uploads are unpinned, so a CID may disappear after the hourly IPFS garbage collection.
  • Slow SSE subscribers (more than 64 queued events) have events dropped with a log line rather than being disconnected.
  • Single instance: the event store and the 256-subscriber cap are per process, with no shared state or horizontal scaling path.
  • Still no automated test coverage for MAX_EVENTS eviction, the upload spool and free-space check, or the download write deadline.

Full Changelog: 1.0.0...1.0.1

1.0.0

Choose a tag to compare

@github-actions github-actions released this 24 Sep 02:25

What's Changed

  • Update Dockerfile to use Kubo 0.40.1 by @jaherron in #4
  • Add Tailwind hover animations and motion polish to upload UI by @besoeasy in #5
  • Update kubo version in Dockerfile to v0.42.0 by @jaherron in #6
  • Add POST /media to strip EXIF and anonymize images before pin by @besoeasy in #10
  • Keep the dashboard as a pin library; leave uploads in examples by @besoeasy in #11
  • Split /upload and /media into separate example pages by @besoeasy in #12
  • Redesign the dashboard as a pin studio with a media library by @besoeasy in #9
  • Enable cross-node IPFS fetch and Docker Compose swarm deploy by @besoeasy in #13

New Contributors

Full Changelog: https://github.com/besoeasy/originless/commits/1.0.0