Nosternity: Give Important Signed Events a Retention Policy

Give an important event a longer life

A signed release announcement or incident note may need to remain available long after the relay session that delivered it. The application needs a retention policy: which records to keep, who may add them, where they live, and how another process retrieves them later.

Nosternity connects Nostr event handling with an Aeternity storage design. Nostr supplies the signed record; the contract supplies an append-only archive with lookup by event ID and insertion order. This can support an auditable history of selected public announcements, article versions or report references.

The public repository contains the contract, relay hooks and integration guide. At the linked revision, the event-store adapter referenced by the supervisor is missing. The design is therefore an integration starting point, not a complete service you can run from that checkout.

Separate delivery from retention

A relay connection answers a delivery question: can an event reach another participant? Retention answers a different question: can that participant retrieve the event later, after the original process or relay session is gone?

Consider a release announcement that refers to a DamageBDD report. Keeping the event preserves the publisher's statement and its references. The report and any supporting attachment still need their own storage policy. An event archive does not automatically preserve every file mentioned by its events.

What the contract implements

The repository's NostrEventStore contract stores the full event record: ID, author, creation time, kind, tags, content and signature. It indexes records globally and by kind. An existing event ID is skipped on insertion, which allows a caller to retry a batch without creating a second copy of the same record.

Only the deploying account can write. Read entry points are public. The contract does not repeat Nostr signature verification on chain; that check belongs to the application before an authorised write. The owner-controlled write path is therefore part of the trust model.

An append-only archive keeps historical IDs. It does not, by itself, implement every Nostr replacement or deletion rule. An application presenting the current version of a record must apply its own query policy over that history. Choose public or explicitly approved records because full event content, rather than only a digest, is committed to the contract.

What the relay implements

The relay verifies an incoming event through damage_nostr_event:verify/1 and inserts a new ID into ETS, Erlang's in-memory table store. It then calls the event-store adapter asynchronously. At startup, its recovery path asks that adapter for pages of stored events and uses them to rebuild the local table.

The order matters. The relay records an ID before requesting persistence, and later submissions of the same ID are suppressed. An ok from the relay therefore establishes local acceptance, not confirmed storage. A persistence failure needs an explicit retry or reconciliation path; submitting the same event again through the relay is not sufficient by itself.

Subscription handling is also incomplete: the filter helper accepts every event, and subscriber broadcasting remains a TODO. The separate WebSocket and HTTP interfaces need their own interoperability checks. This revision does not provide a complete replacement for a public Nostr relay.

Complete the adapter before configuring a service

The event-store integration guide describes a nosternity_event_store process responsible for selection, batching, retries, tracked contract writes and read queries. The supervisor expects to start that process, but the tracked revision does not contain its module, the nosternity_event_store_tests module, or the separate configuration example named in the guide. A clean checkout cannot supply that child process without the missing implementation.

The guide documents an opt-in service with author and kind filters. Its confirmation setting is intended to keep a batch pending until a tracked transaction is confirmed. Treat those settings as the documented adapter contract until the implementation and tests are available in your checkout. Do not infer working queue durability or confirmation behavior from the configuration example alone.

The generic damage_ae_contract helper and the Sophia contract provide useful pieces for finishing this path. The first engineering task is to provide the adapter and verify its lifecycle with the supervisor and relay.

Evaluate retention with a small event set

After completing the adapter, choose one allowed author and a small set of public fixture events. Verify signature rejection, author/kind exclusions, duplicate handling and a confirmed contract write. Read the same event back by ID from a second process.

Then interrupt the service before and after confirmation. Test a full queue, an unavailable chain and an uncertain submission result. The outcome should identify which events are retained, pending, refused or require retry. This is how the project can demonstrate recovery rather than merely successful submission.

The useful adoption result is a record that another process can retrieve after recovery, with its original event identity intact. Retention cost, storage growth and query behavior should be measured for the event classes your application actually intends to keep.

Explore the implementation