# Protected Events The previous chapter introduced behavior tags through expiration: a tag that requests something of the network without touching what the event itself means. NIP-70 adds another, aimed at a different concern. Where expiration asks relays to stop serving an event after a time, NIP-70's `protected` tag asks them to be careful about who they accept it from in the first place. Its shape on the wire is a single, valueless tag: ```json ["-"] ``` That is the whole thing — a tag whose name is a lone dash and which carries no value. Its mere presence is the signal. An event marked this way should be accepted by a relay only when it is published *directly by its author*: the connection delivering it must have authenticated over NIP-42, and the authenticated key must match the event's `pubkey`. ## What the marker asks for A relay that honors NIP-70 runs a small decision when a protected event arrives. If the connection has not authenticated, the relay issues a NIP-42 `AUTH` challenge and rejects the event for now, inviting the client to prove who it is and try again. If the connection *has* authenticated but the authenticated key is not the event's author, the relay rejects the event outright — someone other than the author is trying to push it. Only an authenticated author gets through. As with expiration, the marker is a cooperative request, not a guarantee. A relay is free to ignore it; a relay that has already stored the event keeps it; the signature stays valid regardless. The tag asks well-behaved relays to gate acceptance on authorship. ## The module ```rust {file=coracle-lib/src/lib.rs} pub mod protected; ``` ```rust {file=coracle-lib/src/protected.rs} //! NIP-70 protected events: the valueless `["-"]` marker that asks //! relays to accept an event only from its authenticated author. //! //! This module is the stateless half of NIP-70 — building the tag and //! detecting it. The relay-side enforcement it implies (a NIP-42 `AUTH` //! challenge followed by a pubkey comparison) needs a live, authenticated //! connection and is handled in the relay layer. use crate::events::HasTags; use crate::tags::Tags; ``` ## Detecting the marker Because the tag has no value, detection collapses to a single question: is a tag named `"-"` present? There is nothing to parse, so there is no `Option` and no malformed-versus-absent distinction to draw — the answer is a plain `bool`. `Tags::has` already answers exactly this. ```rust {file=coracle-lib/src/protected.rs} /// Whether the tag set carries the NIP-70 `["-"]` protected marker. /// /// A protected event asks relays to accept it only from its /// authenticated author. This function reports the marker's presence; /// acting on it is the relay's job. pub fn is_protected(tags: &Tags) -> bool { tags.has("-") } ``` ## Building the tag The constructor produces a tag with the name `"-"` and no values. We build it through `Tag::new` like every other tag, passing an empty value iterator so the result is the one-element `["-"]`. ```rust {file=coracle-lib/src/tags.rs} impl Tag { /// Build a NIP-70 `["-"]` protected tag. /// /// The tag has a name (`"-"`) and no values; its presence asks /// relays to accept the event only from its authenticated author. pub fn protected() -> Tag { Tag::new("-", std::iter::empty::()) } } ``` ## Methods on event types A caller holding an event wants to ask whether it is protected without reaching for its tags directly. Following the pattern from expiration, the query is an extension trait bounded on [`HasTags`], so one default method serves every event type. With `coracle_lib::prelude::*` in scope, `event.is_protected()` works on both `HashedEvent` and `Event`. ```rust {file=coracle-lib/src/protected.rs} /// Protected-event queries on any event that carries tags. pub trait EventExtensionProtected: HasTags { /// Whether this event carries the NIP-70 protected marker. fn is_protected(&self) -> bool { is_protected(self.tags()) } } impl EventExtensionProtected for T {} ``` ```rust {file=coracle-lib/src/prelude.rs} pub use crate::protected::EventExtensionProtected; ``` ## Usage patterns **Marking an event protected.** Attach the tag at template construction, just like any other: ```rust let event = EventContent::new() .content("members only") .tags(vec![Tag::protected()]) .kind(1) .stamp(now) .own(pubkey); ``` **Checking on receipt.** A relay or client deciding how to handle an incoming event asks the event directly: ```rust if event.is_protected() { // only accept from an authenticated author } ``` ## What's next Marking an event protected is half of NIP-70; honoring the mark is the other half, and it lives where connections and authentication do. The [Relay Authentication](26-relay-authentication.md) chapter implements the `AUTH` exchange and the author check that turn this tag into a rule. One consequence is worth flagging now: a protected event should not be re-broadcast by being embedded inside someone else's repost, since that would route it past the very acceptance check it asked for. Reposts are their own topic; the protected marker is simply something that logic will need to consult.