Blender add-on: pass CultMeshGrantAllWrites() to its served node #4
3 changed files with 30 additions and 0 deletions
|
|
@ -345,6 +345,12 @@ class BrokkrBlenderTarget:
|
|||
guard = SessionGuard() if require_token else None
|
||||
if guard is not None:
|
||||
server_class = guarded_server_class(server_class, guard)
|
||||
# The add-on's own clients put command intents to this server, so it grants peer writes
|
||||
# explicitly: cultmesh_py refuses them by default, and the session token above is what
|
||||
# authenticates a peer when it is required. A cultmesh_py older than the gate has no
|
||||
# CultMeshGrantAllWrites and already accepts every write.
|
||||
grant_all = getattr(cultmesh, "CultMeshGrantAllWrites", None)
|
||||
authority_args = {"write_authority": grant_all()} if grant_all is not None else {}
|
||||
server = server_class(
|
||||
node=node,
|
||||
verse_catalog=verse_catalog,
|
||||
|
|
@ -354,6 +360,7 @@ class BrokkrBlenderTarget:
|
|||
display_name="Brokkr Blender Editor",
|
||||
max_snapshot_documents=int(max_snapshot_documents),
|
||||
max_snapshot_bytes=int(max_snapshot_bytes),
|
||||
**authority_args,
|
||||
).start()
|
||||
|
||||
endpoint = f"cultnet://{resolved_host}:{server.port}"
|
||||
|
|
|
|||
|
|
@ -51,6 +51,12 @@ write, not only at connect. Reads stay open. `Stop Server`, quitting, or
|
|||
disabling the add-on revokes it; it is held in memory only and never saved to
|
||||
preferences or the cache.
|
||||
|
||||
Peer write scope: CultLib's `cultmesh_py` refuses peer writes unless an authority grants them, and the add-on
|
||||
passes `CultMeshGrantAllWrites()`. A peer that gets past the session token (or any peer, when the token is off) can
|
||||
therefore put or delete **any registered document** on the served node, not only command intents. That is acceptable
|
||||
on loopback, the default; a grant scoped to command intents is future work. Do not combine `Allow Non-Loopback Bind`
|
||||
with the token off.
|
||||
|
||||
Run the tests (no Blender needed; `CULTLIB_PACKAGES` adds the real-server tests):
|
||||
|
||||
```text
|
||||
|
|
|
|||
|
|
@ -261,6 +261,23 @@ class RealServerTests(unittest.TestCase):
|
|||
response = server._handle_connection_message(message, {})
|
||||
self.assertIn("malformed", response[0].get("code", "")) # past the guard, refused by the store as an empty put
|
||||
|
||||
def test_a_peer_put_is_accepted_over_tcp_because_the_target_grants_peer_writes(self):
|
||||
status = self.start()
|
||||
cultmesh, _ = bt._load_cultmesh(PACKAGES)
|
||||
import cultnet_py
|
||||
document = self.target._documents["command_intent"]
|
||||
client = cultmesh.CultMesh.create_client("127.0.0.1", status["port"], timeout_seconds=3.0)
|
||||
with client.subscribe_database(subscription_id="peer", schema_ids=[bt.COMMAND_INTENT_SCHEMA],
|
||||
include_snapshot=False) as subscription:
|
||||
subscription.send(cultnet_py.document_put_raw(
|
||||
message_id="peer-put", key="blender/commands/peer", schema_id=document.catalog_entry().schema_id,
|
||||
stored_at="2026-11-07T00:00:00Z",
|
||||
payload=document.encode_payload({
|
||||
"schema": bt.COMMAND_INTENT_SCHEMA, "commandId": "peer", "action": "createObject"}),
|
||||
))
|
||||
change = subscription.read_next_change() # a refusal would raise CultNetPeerError instead
|
||||
self.assertEqual(change.record_key, "blender/commands/peer")
|
||||
|
||||
def queue_intent(self, key="blender/commands/q1"):
|
||||
node, documents = self.target._open_node(self.cache, PACKAGES)
|
||||
node.database.put(documents["command_intent"], key, {
|
||||
|
|
|
|||
Loading…
Reference in a new issue