ucomm 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. ucomm-0.0.1/LICENSE +28 -0
  2. ucomm-0.0.1/PKG-INFO +116 -0
  3. ucomm-0.0.1/README.md +91 -0
  4. ucomm-0.0.1/pyproject.toml +43 -0
  5. ucomm-0.0.1/setup.cfg +4 -0
  6. ucomm-0.0.1/src/ucomm/__init__.py +56 -0
  7. ucomm-0.0.1/src/ucomm/attention.py +116 -0
  8. ucomm-0.0.1/src/ucomm/bee.py +51 -0
  9. ucomm-0.0.1/src/ucomm/bridges/__init__.py +8 -0
  10. ucomm-0.0.1/src/ucomm/bridges/imap.py +215 -0
  11. ucomm-0.0.1/src/ucomm/contact.py +83 -0
  12. ucomm-0.0.1/src/ucomm/daemon.py +149 -0
  13. ucomm-0.0.1/src/ucomm/encoding.py +51 -0
  14. ucomm-0.0.1/src/ucomm/envelope.py +214 -0
  15. ucomm-0.0.1/src/ucomm/hints.py +64 -0
  16. ucomm-0.0.1/src/ucomm/log.py +100 -0
  17. ucomm-0.0.1/src/ucomm/profiles/__init__.py +7 -0
  18. ucomm-0.0.1/src/ucomm/profiles/chat.py +139 -0
  19. ucomm-0.0.1/src/ucomm/profiles/mail.py +48 -0
  20. ucomm-0.0.1/src/ucomm/rendezvous.py +55 -0
  21. ucomm-0.0.1/src/ucomm/signing.py +68 -0
  22. ucomm-0.0.1/src/ucomm/store.py +105 -0
  23. ucomm-0.0.1/src/ucomm.egg-info/PKG-INFO +116 -0
  24. ucomm-0.0.1/src/ucomm.egg-info/SOURCES.txt +42 -0
  25. ucomm-0.0.1/src/ucomm.egg-info/dependency_links.txt +1 -0
  26. ucomm-0.0.1/src/ucomm.egg-info/requires.txt +12 -0
  27. ucomm-0.0.1/src/ucomm.egg-info/top_level.txt +1 -0
  28. ucomm-0.0.1/tests/test_attention.py +70 -0
  29. ucomm-0.0.1/tests/test_bee_live.py +55 -0
  30. ucomm-0.0.1/tests/test_chat_profile.py +127 -0
  31. ucomm-0.0.1/tests/test_contact.py +74 -0
  32. ucomm-0.0.1/tests/test_daemon.py +178 -0
  33. ucomm-0.0.1/tests/test_encoding.py +45 -0
  34. ucomm-0.0.1/tests/test_envelope.py +24 -0
  35. ucomm-0.0.1/tests/test_genesis_validation.py +86 -0
  36. ucomm-0.0.1/tests/test_hints.py +60 -0
  37. ucomm-0.0.1/tests/test_imap_bridge.py +120 -0
  38. ucomm-0.0.1/tests/test_imap_bridge_live.py +55 -0
  39. ucomm-0.0.1/tests/test_imap_mailbox.py +114 -0
  40. ucomm-0.0.1/tests/test_log.py +105 -0
  41. ucomm-0.0.1/tests/test_mail_profile.py +43 -0
  42. ucomm-0.0.1/tests/test_rendezvous.py +62 -0
  43. ucomm-0.0.1/tests/test_signing.py +62 -0
  44. ucomm-0.0.1/tests/test_store.py +87 -0
ucomm-0.0.1/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Peter Foldiak
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
ucomm-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,116 @@
1
+ Metadata-Version: 2.4
2
+ Name: ucomm
3
+ Version: 0.0.1
4
+ Summary: Universal communicator middleware for Ethereum Swarm (working title)
5
+ Author: Peter Foldiak
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/petfold/ucomm
8
+ Project-URL: Repository, https://github.com/petfold/ucomm
9
+ Project-URL: Issues, https://github.com/petfold/ucomm/issues
10
+ Keywords: swarm,ethereum,web3,bee,messaging,chat,notifications,attention,middleware
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: recordstore[bee]>=0.13.1
15
+ Requires-Dist: swarm-bee<2,>=1.1
16
+ Requires-Dist: imapclient<4,>=3.1
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest>=8; extra == "test"
19
+ Requires-Dist: hypothesis>=6; extra == "test"
20
+ Requires-Dist: mypy>=1.10; extra == "test"
21
+ Requires-Dist: ruff>=0.5; extra == "test"
22
+ Provides-Extra: dev
23
+ Requires-Dist: ucomm[test]; extra == "dev"
24
+ Dynamic: license-file
25
+
26
+ # ucomm — universal communicator middleware for Ethereum Swarm
27
+
28
+ [![tests](https://github.com/petfold/ucomm/actions/workflows/tests.yml/badge.svg)](https://github.com/petfold/ucomm/actions/workflows/tests.yml)
29
+ [![license](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE)
30
+ [![status](https://img.shields.io/badge/status-M2%20in%20progress-yellow)](docs/ROADMAP.md)
31
+
32
+ *Working title. Naming is an open question (see ROADMAP.md, issue N-1).*
33
+
34
+ Every communication app — chat, email, calls, streams, forums, social feeds — moves
35
+ information of some kind from one or more people to one or more people. The apps
36
+ differ in a handful of parameters (topology, interactivity, persistence, privacy,
37
+ invitation granularity), not in principle. Yet on Swarm, as elsewhere, each project
38
+ rebuilds the same primitives from scratch and ships its own silo, its own inbox,
39
+ and its own notification settings.
40
+
41
+ **ucomm** is middleware that factors out the common core:
42
+
43
+ - a **channel kernel**: a channel is a set of per-author append-only logs, a merge
44
+ rule, and a parameter vector fixed at genesis; every event is one signed envelope.
45
+ - an **attention layer**: a first-class control plane for requesting human attention,
46
+ based on Attila Lendvai's priority taxonomy (importance × urgency, ceilings,
47
+ offsets, thresholds), made incentive-compatible with postage costs, attention
48
+ bonds, and local reputation.
49
+ - a **universal inbox**: one local notification daemon per device arbitrates all
50
+ attention requests across all channels and apps; apps become views and policy
51
+ defaults, not silos.
52
+ - a **discovery/recommendation layer**: WoT-weighted collaborative filtering plus
53
+ concept-based (OntoDAG) content filtering, feeding priors into the attention
54
+ engine.
55
+
56
+ Guiding principle: **receiver sovereignty**. Everything a sender or channel declares
57
+ (priority, offsets, importance) is advisory and signed; the receiver's local policy
58
+ engine is the only authority over the receiver's attention.
59
+
60
+ ## Repository layout
61
+
62
+ ```
63
+ docs/DESIGN.md Architecture: two planes, channel kernel, profiles, transports
64
+ docs/ATTENTION.md Attention economy: priority algebra, policy engine, mechanisms
65
+ docs/RECOMMENDATION.md Discovery & collaborative filtering (needs merge from prior notes)
66
+ docs/ROADMAP.md Module decomposition, milestones, initial issues
67
+ docs/USER_GUIDE.md Tutorial: motivation, project overview, install, worked examples
68
+ CLAUDE.md Instructions and invariants for Claude Code sessions
69
+ src/ucomm/ Python package (schema, policy engine, signing, chat profile)
70
+ tests/ pytest suite
71
+ ```
72
+
73
+ ## Status
74
+
75
+ **M0 (schema + algebra) and M1 (known-contact channels over Swarm) are done**
76
+ — see `docs/ROADMAP.md` for the full milestone/issue breakdown. Concretely:
77
+ the envelope/genesis schema with canonical encoding and validation; the
78
+ priority algebra and policy engine with golden decision tests; per-author
79
+ logs with deterministic causal-DAG merge, backed by either an in-memory
80
+ implementation or a real recordstore/Bee adapter; real secp256k1 envelope
81
+ signing (via `swarm-bee`) and out-of-band contact exchange; and a two-party/
82
+ group chat profile exercising all of the above end-to-end, confirmed against
83
+ a live Bee node. New here? Start with `docs/USER_GUIDE.md`.
84
+
85
+ **M2 (notification daemon + universal inbox) is in progress**: a private
86
+ channel directory, a graded active/obsolete dashboard (a projection,
87
+ recomputed on demand — never persisted), read-state aggregation across
88
+ every channel, a push/hint-delivery interface, and the IMAP bridge's
89
+ conversion layer (email → the same envelopes and dashboard, no live
90
+ mailbox yet) are done. Left: an actual hint backend, IMAP's live fetch
91
+ loop, and the Nostr bridge — see `docs/USER_GUIDE.md` sections 11–13 for
92
+ the directory/dashboard/hints/bridge demos. GSOC-based rendezvous
93
+ (unsolicited contact, group discovery) stays behind the `Rendezvous`
94
+ interface, pending the GSOC/pub-sub work by Viktor Tóth and Viktor Trón.
95
+ Broadcast-style 1:N live streaming is explicitly out of scope here —
96
+ Solar Punk Ltd's own `swarm-hls-stream`/`Swarmcast` line already owns that
97
+ (DESIGN.md §5).
98
+
99
+ **On decentralization, read this before evaluating further:** everything
100
+ above runs on Swarm feeds and needs no full node — a light client is
101
+ enough. Real-time push notification and unsolicited-contact discovery (PSS
102
+ and GSOC) are a different story: both require a full node, yours or a
103
+ relay's, and that relay sees traffic metadata, which is a real
104
+ centralization point still unresolved (not just a caveat) — see
105
+ `docs/DESIGN.md` §5 and §11. Nothing here should be evaluated as "fully
106
+ decentralized" without that qualification.
107
+
108
+ ## Related projects
109
+
110
+ - **recordstore** (github.com/petfold/recordstore) — candidate persistence substrate
111
+ for author logs (versioned key → record over Swarm; POT track).
112
+ - **swarmfs** (github.com/petfold/swarmfs) — fsspec backend, useful for payload blobs.
113
+ - **OntoDAG / mdl-fca** — semantic concept DAG; content-based half of the
114
+ recommendation layer.
115
+ - Attila Lendvai, *Computer aided human communication*
116
+ (codeberg.org/attila.lendvai/publications) — source of the attention taxonomy.
ucomm-0.0.1/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # ucomm — universal communicator middleware for Ethereum Swarm
2
+
3
+ [![tests](https://github.com/petfold/ucomm/actions/workflows/tests.yml/badge.svg)](https://github.com/petfold/ucomm/actions/workflows/tests.yml)
4
+ [![license](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE)
5
+ [![status](https://img.shields.io/badge/status-M2%20in%20progress-yellow)](docs/ROADMAP.md)
6
+
7
+ *Working title. Naming is an open question (see ROADMAP.md, issue N-1).*
8
+
9
+ Every communication app — chat, email, calls, streams, forums, social feeds — moves
10
+ information of some kind from one or more people to one or more people. The apps
11
+ differ in a handful of parameters (topology, interactivity, persistence, privacy,
12
+ invitation granularity), not in principle. Yet on Swarm, as elsewhere, each project
13
+ rebuilds the same primitives from scratch and ships its own silo, its own inbox,
14
+ and its own notification settings.
15
+
16
+ **ucomm** is middleware that factors out the common core:
17
+
18
+ - a **channel kernel**: a channel is a set of per-author append-only logs, a merge
19
+ rule, and a parameter vector fixed at genesis; every event is one signed envelope.
20
+ - an **attention layer**: a first-class control plane for requesting human attention,
21
+ based on Attila Lendvai's priority taxonomy (importance × urgency, ceilings,
22
+ offsets, thresholds), made incentive-compatible with postage costs, attention
23
+ bonds, and local reputation.
24
+ - a **universal inbox**: one local notification daemon per device arbitrates all
25
+ attention requests across all channels and apps; apps become views and policy
26
+ defaults, not silos.
27
+ - a **discovery/recommendation layer**: WoT-weighted collaborative filtering plus
28
+ concept-based (OntoDAG) content filtering, feeding priors into the attention
29
+ engine.
30
+
31
+ Guiding principle: **receiver sovereignty**. Everything a sender or channel declares
32
+ (priority, offsets, importance) is advisory and signed; the receiver's local policy
33
+ engine is the only authority over the receiver's attention.
34
+
35
+ ## Repository layout
36
+
37
+ ```
38
+ docs/DESIGN.md Architecture: two planes, channel kernel, profiles, transports
39
+ docs/ATTENTION.md Attention economy: priority algebra, policy engine, mechanisms
40
+ docs/RECOMMENDATION.md Discovery & collaborative filtering (needs merge from prior notes)
41
+ docs/ROADMAP.md Module decomposition, milestones, initial issues
42
+ docs/USER_GUIDE.md Tutorial: motivation, project overview, install, worked examples
43
+ CLAUDE.md Instructions and invariants for Claude Code sessions
44
+ src/ucomm/ Python package (schema, policy engine, signing, chat profile)
45
+ tests/ pytest suite
46
+ ```
47
+
48
+ ## Status
49
+
50
+ **M0 (schema + algebra) and M1 (known-contact channels over Swarm) are done**
51
+ — see `docs/ROADMAP.md` for the full milestone/issue breakdown. Concretely:
52
+ the envelope/genesis schema with canonical encoding and validation; the
53
+ priority algebra and policy engine with golden decision tests; per-author
54
+ logs with deterministic causal-DAG merge, backed by either an in-memory
55
+ implementation or a real recordstore/Bee adapter; real secp256k1 envelope
56
+ signing (via `swarm-bee`) and out-of-band contact exchange; and a two-party/
57
+ group chat profile exercising all of the above end-to-end, confirmed against
58
+ a live Bee node. New here? Start with `docs/USER_GUIDE.md`.
59
+
60
+ **M2 (notification daemon + universal inbox) is in progress**: a private
61
+ channel directory, a graded active/obsolete dashboard (a projection,
62
+ recomputed on demand — never persisted), read-state aggregation across
63
+ every channel, a push/hint-delivery interface, and the IMAP bridge's
64
+ conversion layer (email → the same envelopes and dashboard, no live
65
+ mailbox yet) are done. Left: an actual hint backend, IMAP's live fetch
66
+ loop, and the Nostr bridge — see `docs/USER_GUIDE.md` sections 11–13 for
67
+ the directory/dashboard/hints/bridge demos. GSOC-based rendezvous
68
+ (unsolicited contact, group discovery) stays behind the `Rendezvous`
69
+ interface, pending the GSOC/pub-sub work by Viktor Tóth and Viktor Trón.
70
+ Broadcast-style 1:N live streaming is explicitly out of scope here —
71
+ Solar Punk Ltd's own `swarm-hls-stream`/`Swarmcast` line already owns that
72
+ (DESIGN.md §5).
73
+
74
+ **On decentralization, read this before evaluating further:** everything
75
+ above runs on Swarm feeds and needs no full node — a light client is
76
+ enough. Real-time push notification and unsolicited-contact discovery (PSS
77
+ and GSOC) are a different story: both require a full node, yours or a
78
+ relay's, and that relay sees traffic metadata, which is a real
79
+ centralization point still unresolved (not just a caveat) — see
80
+ `docs/DESIGN.md` §5 and §11. Nothing here should be evaluated as "fully
81
+ decentralized" without that qualification.
82
+
83
+ ## Related projects
84
+
85
+ - **recordstore** (github.com/petfold/recordstore) — candidate persistence substrate
86
+ for author logs (versioned key → record over Swarm; POT track).
87
+ - **swarmfs** (github.com/petfold/swarmfs) — fsspec backend, useful for payload blobs.
88
+ - **OntoDAG / mdl-fca** — semantic concept DAG; content-based half of the
89
+ recommendation layer.
90
+ - Attila Lendvai, *Computer aided human communication*
91
+ (codeberg.org/attila.lendvai/publications) — source of the attention taxonomy.
@@ -0,0 +1,43 @@
1
+ [project.urls]
2
+ Homepage = "https://github.com/petfold/ucomm"
3
+ Repository = "https://github.com/petfold/ucomm"
4
+ Issues = "https://github.com/petfold/ucomm/issues"
5
+
6
+ [build-system]
7
+ requires = ["setuptools>=68"]
8
+ build-backend = "setuptools.build_meta"
9
+
10
+ [project]
11
+ name = "ucomm"
12
+ version = "0.0.1"
13
+ description = "Universal communicator middleware for Ethereum Swarm (working title)"
14
+ requires-python = ">=3.11"
15
+ keywords = ["swarm", "ethereum", "web3", "bee", "messaging", "chat", "notifications", "attention", "middleware"]
16
+ readme = "README.md"
17
+ license = { text = "BSD-3-Clause" }
18
+ authors = [{ name = "Peter Foldiak" }]
19
+ dependencies = ["recordstore[bee]>=0.13.1", "swarm-bee>=1.1,<2", "imapclient>=3.1,<4"]
20
+
21
+ [project.optional-dependencies]
22
+ test = ["pytest>=8", "hypothesis>=6", "mypy>=1.10", "ruff>=0.5"]
23
+ dev = ["ucomm[test]"] # back-compat alias; [test] is the stack-wide name
24
+
25
+ [tool.setuptools.packages.find]
26
+ where = ["src"]
27
+
28
+ [tool.pytest.ini_options]
29
+ testpaths = ["tests"]
30
+
31
+ [tool.mypy]
32
+ strict = true
33
+
34
+ [[tool.mypy.overrides]]
35
+ module = "recordstore.*"
36
+ ignore_missing_imports = true
37
+
38
+ [[tool.mypy.overrides]]
39
+ module = "imapclient.*"
40
+ ignore_missing_imports = true
41
+
42
+ [tool.ruff]
43
+ line-length = 96
ucomm-0.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,56 @@
1
+ """ucomm: universal communicator middleware for Ethereum Swarm (working title)."""
2
+
3
+ from .attention import Decision, Intensity, PolicyState, SenderContext, decide
4
+ from .contact import ContactCard, make_contact_card, verify_contact_card
5
+ from .daemon import (
6
+ ChannelDirectory,
7
+ Dashboard,
8
+ DashboardItem,
9
+ DirectoryEntry,
10
+ build_dashboard,
11
+ directory_read_state,
12
+ )
13
+ from .envelope import AttentionClaim, Envelope, EventKind, Genesis, GenesisError, TimeWindow
14
+ from .hints import HintSink, HintSource, InMemoryHints
15
+ from .log import AuthorLog, merge_causal, read_state
16
+ from .rendezvous import InMemoryRendezvous, Rendezvous
17
+ from .signing import InvalidSignature, address_of, sign_envelope, verify_envelope
18
+ from .store import RecordStoreAuthorLog, envelope_to_record, record_to_envelope
19
+
20
+ __all__ = [
21
+ "AttentionClaim",
22
+ "AuthorLog",
23
+ "ChannelDirectory",
24
+ "ContactCard",
25
+ "Dashboard",
26
+ "DashboardItem",
27
+ "Decision",
28
+ "DirectoryEntry",
29
+ "Envelope",
30
+ "EventKind",
31
+ "Genesis",
32
+ "GenesisError",
33
+ "HintSink",
34
+ "HintSource",
35
+ "InMemoryHints",
36
+ "InMemoryRendezvous",
37
+ "Intensity",
38
+ "InvalidSignature",
39
+ "PolicyState",
40
+ "RecordStoreAuthorLog",
41
+ "Rendezvous",
42
+ "SenderContext",
43
+ "TimeWindow",
44
+ "address_of",
45
+ "build_dashboard",
46
+ "decide",
47
+ "directory_read_state",
48
+ "envelope_to_record",
49
+ "make_contact_card",
50
+ "merge_causal",
51
+ "read_state",
52
+ "record_to_envelope",
53
+ "sign_envelope",
54
+ "verify_contact_card",
55
+ "verify_envelope",
56
+ ]
@@ -0,0 +1,116 @@
1
+ """Attention layer: priority algebra and policy engine (ATTENTION.md).
2
+
3
+ Pure and deterministic by design: decide(envelope, policy, now) -> Decision is a
4
+ function of its arguments only. All mutable state (ceilings, offsets, thresholds,
5
+ reputation ratchet) lives in PolicyState, owned by the daemon.
6
+
7
+ Receiver sovereignty: this module consumes AttentionClaim data and never
8
+ constructs envelopes. One-way dependency: attention -> envelope, never reverse.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections.abc import Mapping
14
+ from dataclasses import dataclass, field
15
+ from enum import IntEnum
16
+
17
+ from .envelope import Envelope, EventKind, PubKey
18
+
19
+
20
+ class Intensity(IntEnum):
21
+ """Graded notification output (ATTENTION.md section 3)."""
22
+
23
+ FILED = 0 # silently filed; dashboard history only
24
+ BADGE = 1 # badge / dashboard, no interruption
25
+ SOFT = 2 # ring once / vibrate only
26
+ FULL = 3 # full alert
27
+ BREAKTHROUGH = 4 # overrides DND
28
+
29
+
30
+ # Residual thresholds for grading, in log-scale priority units.
31
+ SOFT_BAND = 5 # |residual| <= SOFT_BAND counts as "near threshold"
32
+ BREAKTHROUGH_MARGIN = 20
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class PolicyState:
37
+ """Receiver-local policy snapshot. Never published, never negotiated."""
38
+
39
+ threshold: int # current global threshold; DND = high value
40
+ default_ceiling_known: int = 40
41
+ default_ceiling_stranger: int = 0
42
+ wot_ceiling_slope: int = 10 # ceiling = base - slope * wot_distance (ATTENTION 5)
43
+ contact_ceilings: Mapping[PubKey, int] = field(default_factory=dict)
44
+ channel_offsets: Mapping[str, int] = field(default_factory=dict)
45
+ endpoint_offset: int = 0 # e.g. raised while in an interactive session
46
+ bond_credit: int = 10 # credibility bonus when valid collateral attached
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class SenderContext:
51
+ """What the receiver knows about the sender (from identity/WoT + recsys)."""
52
+
53
+ known_contact: bool
54
+ wot_distance: int | None = None # None = no path
55
+ recsys_prior: int = 0 # advisory prior from RECOMMENDATION.md section 6
56
+ collateral_valid: bool = False
57
+
58
+
59
+ @dataclass(frozen=True)
60
+ class Decision:
61
+ intensity: Intensity
62
+ effective_priority: int
63
+ residual: int
64
+ ceiling_applied: int
65
+
66
+
67
+ def ceiling_for(sender: PubKey, ctx: SenderContext, policy: PolicyState) -> int:
68
+ """Per-sender priority ceiling (ATTENTION.md sections 2, 5)."""
69
+ if sender in policy.contact_ceilings:
70
+ return policy.contact_ceilings[sender]
71
+ if ctx.known_contact:
72
+ return policy.default_ceiling_known
73
+ if ctx.wot_distance is not None:
74
+ base = policy.default_ceiling_known
75
+ return max(
76
+ policy.default_ceiling_stranger,
77
+ base - policy.wot_ceiling_slope * ctx.wot_distance + ctx.recsys_prior,
78
+ )
79
+ return policy.default_ceiling_stranger + ctx.recsys_prior
80
+
81
+
82
+ def decide(env: Envelope, ctx: SenderContext, policy: PolicyState, now: float) -> Decision:
83
+ """Map one control-plane envelope to a graded notification intensity.
84
+
85
+ Pure function: (envelope, sender context, policy state, clock) -> Decision.
86
+ """
87
+ if env.kind is not EventKind.INVITATION or env.attention is None:
88
+ return Decision(Intensity.FILED, 0, -(10**6), 0)
89
+
90
+ claim = env.attention
91
+ if not claim.relevance.active(now):
92
+ return Decision(Intensity.FILED, 0, -(10**6), 0) # expired -> timeline
93
+
94
+ cap = ceiling_for(env.author, ctx, policy)
95
+ credibility = policy.bond_credit if ctx.collateral_valid else 0
96
+
97
+ effective = (
98
+ min(claim.claimed_priority, cap)
99
+ + policy.channel_offsets.get(env.channel, 0)
100
+ + policy.endpoint_offset
101
+ + credibility
102
+ )
103
+ residual = effective - policy.threshold
104
+
105
+ if residual > BREAKTHROUGH_MARGIN:
106
+ intensity = Intensity.BREAKTHROUGH
107
+ elif residual > SOFT_BAND:
108
+ intensity = Intensity.FULL
109
+ elif residual > 0:
110
+ intensity = Intensity.SOFT
111
+ elif residual > -SOFT_BAND:
112
+ intensity = Intensity.BADGE
113
+ else:
114
+ intensity = Intensity.FILED
115
+
116
+ return Decision(intensity, effective, residual, cap)
@@ -0,0 +1,51 @@
1
+ """Real Swarm-backed author logs (M1).
2
+
3
+ Thin adapter from `RecordStoreAuthorLog` (`ucomm.store`) to recordstore's Bee
4
+ backends -- `BeeBytesStore` + `SwarmFeedPointer` -- instead of the in-memory
5
+ ones used for M0 prototyping. This is the "drop-in swap, not a rewrite"
6
+ ROADMAP.md promised: `RecordStoreAuthorLog` only ever needed a
7
+ `recordstore.RecordStore`; it never knew or cared whether that store was
8
+ backed by memory or a live Bee node.
9
+
10
+ Feeds need an IMMUTABLE postage batch (see CLAUDE.md, "Swarm facts to
11
+ respect"). This module never buys one -- pass an existing usable batch id
12
+ (`GET /stamps` on the target node), bought e.g. with
13
+ `POST /stamps/{amount}/{depth}?immutable=true` or swarmfs's `StampManager`.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from recordstore import BeeBytesStore, RecordStore, SwarmFeedPointer
19
+
20
+ from .envelope import ChannelId, PubKey
21
+ from .signing import address_of
22
+ from .store import RecordStoreAuthorLog
23
+
24
+
25
+ def feed_topic(channel: ChannelId, author: PubKey) -> str:
26
+ """Feed topic namespacing one author's per-channel log."""
27
+ return f"ucomm/log/{channel}/{author}"
28
+
29
+
30
+ def open_author_feed_log(
31
+ api_url: str, channel: ChannelId, *, signer_hex: str, postage_batch_id: str,
32
+ ) -> tuple[RecordStore, RecordStoreAuthorLog]:
33
+ """Open `signer_hex`'s own log for `channel`, backed by a real Swarm feed.
34
+
35
+ `signer_hex` is the author's private key (hex); the feed owner (and
36
+ hence `Envelope.author`, via `ucomm.signing.address_of`) is derived from
37
+ it. Returns `(store, log)`: append through `log`, then call
38
+ `store.commit()` to actually publish -- appends are staged locally until
39
+ then, same as any other `RecordStore` (issue K-4's batching model, not a
40
+ new one for Bee).
41
+ """
42
+ from bee.swarm.keys import PrivateKey
43
+
44
+ author: PubKey = address_of(PrivateKey.from_hex(signer_hex))
45
+ pointer = SwarmFeedPointer(
46
+ api_url, feed_topic(channel, author),
47
+ signer=signer_hex, postage_batch_id=postage_batch_id,
48
+ )
49
+ bytes_store = BeeBytesStore(api_url, postage_batch_id=postage_batch_id)
50
+ store = RecordStore(bytes_store, pointer=pointer)
51
+ return store, RecordStoreAuthorLog(store, channel, author)
@@ -0,0 +1,8 @@
1
+ """Bridges: adapters converting external protocols into ucomm envelopes.
2
+
3
+ DESIGN.md section 10: every bridge is an inbox adapter into the same
4
+ envelope schema and policy engine ucomm uses natively -- the "attention
5
+ firewall" is a unified inbox across everything, not just Swarm-native
6
+ channels. Kept out of the top-level `ucomm` namespace, same reasoning as
7
+ `ucomm.profiles`: a layer above the kernel, not part of it.
8
+ """