Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
All notable changes to `overlay-social` (Python). Mirrors
`@overlay-social/sdk` (TypeScript) one-to-one.

## 0.3.0 — 2026-07-29

### Added
- `SourceHandle` (TypedDict) — a source-scoped alias for a `PeckRow`'s
`author`, present at `row["source_handle"]` on `app == "zanaadu"` rows
that own an on-chain "user number" (`{namespace: "zanaadu", value: "@14",
number: 14, kind: "user_number", membership_proof: "none"}`, plus
`numbers` when a key owns more than one). Additive: comes alongside
`author`, never replaces it, and is absent (not `None`) when the author
has no number. `membership_proof: "none"` is explicit that the value is
read from Zanaadu's registry, not verified against their Merkle tree.
See `peck-overlay-schema/ZANAADU_POSTANCHOR_FORMAT.md` §13.

## 0.2.0 — 2026-06-12

### Added
Expand Down
4 changes: 3 additions & 1 deletion overlay_social/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@
ProfileRow,
ProfileState,
ResolvedIdentity,
SourceHandle,
ThreadResponse,
TopicAnchor,
TopicState,
)

__version__ = "0.2.0"
__version__ = "0.3.0"

__all__ = [
"__version__",
Expand All @@ -69,4 +70,5 @@
"FeedResponse",
"ThreadResponse",
"PeckRow",
"SourceHandle",
]
50 changes: 48 additions & 2 deletions overlay_social/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,65 @@
(``displayName``, ``avatarRef``, ``stateRoot``) and snake_case (``minted_at``);
the ``from_dict`` helpers preserve that verbatim rather than silently
normalizing a real contract difference. Open-ended rows (``PeckRow``) stay
plain dicts because the indexer rides extra MAP keys.
plain dicts because the indexer rides extra MAP keys — the one typed
exception is ``SourceHandle``, documented separately below because it is a
nested value worth a real shape, not a top-level response.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any
from typing import Any, Literal, TypedDict

# A peck row (/v1/feed, /v1/post/:txid) is intentionally an open dict — the
# indexer carries arbitrary extra MAP keys, so we do not lock its shape.
PeckRow = dict[str, Any]


class _SourceHandleRequired(TypedDict):
namespace: Literal["zanaadu"]
value: str
number: int
kind: Literal["user_number"]
membership_proof: Literal["none"]


class SourceHandle(_SourceHandleRequired, total=False):
"""Feed shape of one foreign-namespace alias (mirrors `SourceHandle` in
``@overlay-social/sdk`` one-to-one).

May appear at ``PeckRow["source_handle"]``. Today the only source is
Zanaadu: it sells on-chain "user numbers" (an on-chain collectibles
registry), and rows with ``app == "zanaadu"`` carry the holder's number
here. See ``peck-overlay-schema/ZANAADU_POSTANCHOR_FORMAT.md`` §13 for
the full derivation and its proof gap.

``namespace`` is mandatory and comes first so the value can never be
shown bare and mistaken for a peck handle — render it qualified, e.g.
``@14 · zanaadu``.

Rules (normative):

- Comes IN ADDITION TO ``author`` (the key). Never replaces it.
- Omitted from the row entirely (never ``None``, never ``""``) when the
author has no alias at the source — absence is a valid, measured
state there, not a gap to fill with a guess.
- ``membership_proof: "none"`` means the value was READ from the
source's own state (here: a registry counter, cross-checked 7/7
against Zanaadu's own UI) but the source's stronger commitment
(their sparse Merkle tree) is NOT verified by us — the root is
legible, not reproducible. It is not a claim that the number itself
is unreliable.
- ``numbers`` (all numbers this key owns at the source, ascending) is
only present when there is more than one; ``value``/``number`` are
then the lowest. Tie-break for "which number is primary" when a key
owns several is NOT proven on-chain (§13.7) — lowest was chosen for
stability over time, not because it is confirmed canonical.
"""

numbers: list[int]


@dataclass(frozen=True)
class ResolvedIdentity:
"""One entry from POST /v1/identities/resolve, keyed by the exact input."""
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "overlay-social"
version = "0.2.0"
version = "0.3.0"
description = "Read-only Python client for overlay.peck.to — identity resolution, profiles, feed and overlay state over the canonical BSV/BRC-100 social overlay."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
Loading