Add X-Persist header to control client-side local storage

Introduces a new X-Persist (or Persist) publish parameter. When set to
"no", the server includes "persist": false in the broadcast JSON payload,
signalling clients that they should not save the message to local history.

This is intentionally separate from X-Cache (server-side caching) so that
publishers can control client persistence independently — e.g. ephemeral
clipboard sync messages that should be delivered but not stored.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Eugene Shtoka 2026-05-04 14:55:51 +03:00
parent 802c0a4c30
commit 434ebbf178
3 changed files with 12 additions and 0 deletions

View file

@ -47,6 +47,7 @@ type Message struct {
PollID string `json:"poll_id,omitempty"`
ContentType string `json:"content_type,omitempty"` // text/plain by default (if empty), or text/markdown
Encoding string `json:"encoding,omitempty"` // Empty for raw UTF-8, or "base64" for encoded bytes
Persist *bool `json:"persist,omitempty"` // If false, clients should not save the message to local history
Sender netip.Addr `json:"-"` // IP address of uploader, used for rate limiting
User string `json:"-"` // UserID of the uploader, used to associated attachments
}

View file

@ -1166,6 +1166,7 @@ func (s *Server) parsePublishParams(r *http.Request, m *model.Message) (cache bo
}
}
cache = readBoolParam(r, true, "x-cache", "cache")
m.Persist = readOptionalBoolParam(r, "x-persist", "persist")
firebase = readBoolParam(r, true, "x-firebase", "firebase")
m.Title = readParam(r, "x-title", "title", "t")
m.Click = readParam(r, "x-click", "click")

View file

@ -30,6 +30,16 @@ var (
forwardedHeaderRegex = regexp.MustCompile(`(?i)\bfor="?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[0-9a-f:]+])(?::\d+)?"?`)
)
// readOptionalBoolParam returns nil if the param is not set, otherwise a pointer to the parsed bool value.
func readOptionalBoolParam(r *http.Request, names ...string) *bool {
value := strings.ToLower(readParam(r, names...))
if value == "" || !isBoolValue(value) {
return nil
}
b := toBool(value)
return &b
}
func readBoolParam(r *http.Request, defaultValue bool, names ...string) bool {
value := strings.ToLower(readParam(r, names...))
if value == "" {