dosync 0.5.0__tar.gz → 0.6.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 (156) hide show
  1. {dosync-0.5.0 → dosync-0.6.1}/PKG-INFO +136 -4
  2. {dosync-0.5.0 → dosync-0.6.1}/README.md +135 -3
  3. {dosync-0.5.0 → dosync-0.6.1}/dosync/__init__.py +1 -1
  4. dosync-0.6.1/dosync/adapter_drafting.py +385 -0
  5. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/__init__.py +19 -0
  6. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/homeassistant.py +2 -1
  7. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/matter.py +2 -1
  8. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/mqtt.py +1 -1
  9. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/notifications.py +1 -1
  10. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/shelly.py +2 -1
  11. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/wiz.py +12 -2
  12. {dosync-0.5.0 → dosync-0.6.1}/dosync/dashboard.html +263 -9
  13. {dosync-0.5.0 → dosync-0.6.1}/dosync/declarative.py +17 -0
  14. {dosync-0.5.0 → dosync-0.6.1}/dosync/discoverers.py +12 -0
  15. {dosync-0.5.0 → dosync-0.6.1}/dosync/discoverers_mdns.py +64 -3
  16. {dosync-0.5.0 → dosync-0.6.1}/dosync/discoverers_ssdp.py +51 -5
  17. {dosync-0.5.0 → dosync-0.6.1}/dosync/discovery.py +19 -2
  18. {dosync-0.5.0 → dosync-0.6.1}/dosync/manage.py +139 -0
  19. {dosync-0.5.0 → dosync-0.6.1}/dosync/mcp_server.py +121 -2
  20. {dosync-0.5.0 → dosync-0.6.1}/dosync/models.py +45 -1
  21. {dosync-0.5.0 → dosync-0.6.1}/dosync/server.py +164 -6
  22. dosync-0.6.1/dosync/templates/adapter-draft-prompt.md +49 -0
  23. {dosync-0.5.0 → dosync-0.6.1}/dosync.egg-info/PKG-INFO +136 -4
  24. {dosync-0.5.0 → dosync-0.6.1}/dosync.egg-info/SOURCES.txt +5 -0
  25. {dosync-0.5.0 → dosync-0.6.1}/pyproject.toml +5 -1
  26. dosync-0.6.1/tests/test_adapter_drafting.py +676 -0
  27. {dosync-0.5.0 → dosync-0.6.1}/tests/test_adapters.py +63 -0
  28. dosync-0.6.1/tests/test_dashboard_tells_the_truth.py +206 -0
  29. {dosync-0.5.0 → dosync-0.6.1}/tests/test_deployment_env_contract.py +33 -0
  30. dosync-0.6.1/tests/test_device_identity.py +157 -0
  31. dosync-0.6.1/tests/test_documentation_is_navigable.py +189 -0
  32. {dosync-0.5.0 → dosync-0.6.1}/tests/test_no_operator_data.py +80 -0
  33. {dosync-0.5.0 → dosync-0.6.1}/tests/test_third_party_adapters.py +3 -1
  34. {dosync-0.5.0 → dosync-0.6.1}/tests/test_transport_discoverers.py +182 -0
  35. dosync-0.5.0/tests/test_dashboard_tells_the_truth.py +0 -68
  36. {dosync-0.5.0 → dosync-0.6.1}/LICENSE +0 -0
  37. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/ble.py +0 -0
  38. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/declarative.py +0 -0
  39. {dosync-0.5.0 → dosync-0.6.1}/dosync/adapters/mavlink.py +0 -0
  40. {dosync-0.5.0 → dosync-0.6.1}/dosync/audit_backup.py +0 -0
  41. {dosync-0.5.0 → dosync-0.6.1}/dosync/auth.py +0 -0
  42. {dosync-0.5.0 → dosync-0.6.1}/dosync/auth_fastapi.py +0 -0
  43. {dosync-0.5.0 → dosync-0.6.1}/dosync/cert_signing.py +0 -0
  44. {dosync-0.5.0 → dosync-0.6.1}/dosync/certify.py +0 -0
  45. {dosync-0.5.0 → dosync-0.6.1}/dosync/cli.py +0 -0
  46. {dosync-0.5.0 → dosync-0.6.1}/dosync/composite_operations.py +0 -0
  47. {dosync-0.5.0 → dosync-0.6.1}/dosync/config_reference.py +0 -0
  48. {dosync-0.5.0 → dosync-0.6.1}/dosync/db.py +0 -0
  49. {dosync-0.5.0 → dosync-0.6.1}/dosync/device_arbiter.py +0 -0
  50. {dosync-0.5.0 → dosync-0.6.1}/dosync/ed25519_pure.py +0 -0
  51. {dosync-0.5.0 → dosync-0.6.1}/dosync/examples/__init__.py +0 -0
  52. {dosync-0.5.0 → dosync-0.6.1}/dosync/examples/declarative/3d-printer.yaml +0 -0
  53. {dosync-0.5.0 → dosync-0.6.1}/dosync/examples/declarative/air-conditioner.yaml +0 -0
  54. {dosync-0.5.0 → dosync-0.6.1}/dosync/examples/declarative/building-lighting.json +0 -0
  55. {dosync-0.5.0 → dosync-0.6.1}/dosync/examples/declarative/industrial-conveyor.yaml +0 -0
  56. {dosync-0.5.0 → dosync-0.6.1}/dosync/examples/declarative/light-generic.yaml +0 -0
  57. {dosync-0.5.0 → dosync-0.6.1}/dosync/examples/declarative/television.yaml +0 -0
  58. {dosync-0.5.0 → dosync-0.6.1}/dosync/executor.py +0 -0
  59. {dosync-0.5.0 → dosync-0.6.1}/dosync/geo.py +0 -0
  60. {dosync-0.5.0 → dosync-0.6.1}/dosync/hub.py +0 -0
  61. {dosync-0.5.0 → dosync-0.6.1}/dosync/hub_monitor.py +0 -0
  62. {dosync-0.5.0 → dosync-0.6.1}/dosync/lightweight.py +0 -0
  63. {dosync-0.5.0 → dosync-0.6.1}/dosync/metrics.py +0 -0
  64. {dosync-0.5.0 → dosync-0.6.1}/dosync/operation_guards.py +0 -0
  65. {dosync-0.5.0 → dosync-0.6.1}/dosync/operation_supervisor.py +0 -0
  66. {dosync-0.5.0 → dosync-0.6.1}/dosync/operations.py +0 -0
  67. {dosync-0.5.0 → dosync-0.6.1}/dosync/paths.py +0 -0
  68. {dosync-0.5.0 → dosync-0.6.1}/dosync/plugins.py +0 -0
  69. {dosync-0.5.0 → dosync-0.6.1}/dosync/policies.py +0 -0
  70. {dosync-0.5.0 → dosync-0.6.1}/dosync/policy_config.py +0 -0
  71. {dosync-0.5.0 → dosync-0.6.1}/dosync/py.typed +0 -0
  72. {dosync-0.5.0 → dosync-0.6.1}/dosync/reconciler.py +0 -0
  73. {dosync-0.5.0 → dosync-0.6.1}/dosync/route_composer.py +0 -0
  74. {dosync-0.5.0 → dosync-0.6.1}/dosync/security.py +0 -0
  75. {dosync-0.5.0 → dosync-0.6.1}/dosync/spec_coverage.py +0 -0
  76. {dosync-0.5.0 → dosync-0.6.1}/dosync/validation.py +0 -0
  77. {dosync-0.5.0 → dosync-0.6.1}/dosync.egg-info/dependency_links.txt +0 -0
  78. {dosync-0.5.0 → dosync-0.6.1}/dosync.egg-info/entry_points.txt +0 -0
  79. {dosync-0.5.0 → dosync-0.6.1}/dosync.egg-info/requires.txt +0 -0
  80. {dosync-0.5.0 → dosync-0.6.1}/dosync.egg-info/top_level.txt +0 -0
  81. {dosync-0.5.0 → dosync-0.6.1}/setup.cfg +0 -0
  82. {dosync-0.5.0 → dosync-0.6.1}/tests/test_audit_archive.py +0 -0
  83. {dosync-0.5.0 → dosync-0.6.1}/tests/test_audit_backup.py +0 -0
  84. {dosync-0.5.0 → dosync-0.6.1}/tests/test_audit_chain_integrity.py +0 -0
  85. {dosync-0.5.0 → dosync-0.6.1}/tests/test_audit_provenance.py +0 -0
  86. {dosync-0.5.0 → dosync-0.6.1}/tests/test_auth.py +0 -0
  87. {dosync-0.5.0 → dosync-0.6.1}/tests/test_ble_adapter.py +0 -0
  88. {dosync-0.5.0 → dosync-0.6.1}/tests/test_certification_honesty.py +0 -0
  89. {dosync-0.5.0 → dosync-0.6.1}/tests/test_claim_state_machine.py +0 -0
  90. {dosync-0.5.0 → dosync-0.6.1}/tests/test_composite_operations.py +0 -0
  91. {dosync-0.5.0 → dosync-0.6.1}/tests/test_composite_orchestration.py +0 -0
  92. {dosync-0.5.0 → dosync-0.6.1}/tests/test_composition_kind_db.py +0 -0
  93. {dosync-0.5.0 → dosync-0.6.1}/tests/test_composition_kind_endpoint.py +0 -0
  94. {dosync-0.5.0 → dosync-0.6.1}/tests/test_composition_routing.py +0 -0
  95. {dosync-0.5.0 → dosync-0.6.1}/tests/test_db.py +0 -0
  96. {dosync-0.5.0 → dosync-0.6.1}/tests/test_declarative_adapters.py +0 -0
  97. {dosync-0.5.0 → dosync-0.6.1}/tests/test_declarative_quarantine.py +0 -0
  98. {dosync-0.5.0 → dosync-0.6.1}/tests/test_deployment_layout.py +0 -0
  99. {dosync-0.5.0 → dosync-0.6.1}/tests/test_device_health.py +0 -0
  100. {dosync-0.5.0 → dosync-0.6.1}/tests/test_device_heartbeat.py +0 -0
  101. {dosync-0.5.0 → dosync-0.6.1}/tests/test_direct_action_governance.py +0 -0
  102. {dosync-0.5.0 → dosync-0.6.1}/tests/test_discovery_adoption.py +0 -0
  103. {dosync-0.5.0 → dosync-0.6.1}/tests/test_drone_policies.py +0 -0
  104. {dosync-0.5.0 → dosync-0.6.1}/tests/test_ed25519_pure.py +0 -0
  105. {dosync-0.5.0 → dosync-0.6.1}/tests/test_emergency_preemption.py +0 -0
  106. {dosync-0.5.0 → dosync-0.6.1}/tests/test_env_loading_is_explicit.py +0 -0
  107. {dosync-0.5.0 → dosync-0.6.1}/tests/test_evaluation_metrics.py +0 -0
  108. {dosync-0.5.0 → dosync-0.6.1}/tests/test_event_loop_migration.py +0 -0
  109. {dosync-0.5.0 → dosync-0.6.1}/tests/test_explain_consistency.py +0 -0
  110. {dosync-0.5.0 → dosync-0.6.1}/tests/test_explain_resolve_parity.py +0 -0
  111. {dosync-0.5.0 → dosync-0.6.1}/tests/test_geo.py +0 -0
  112. {dosync-0.5.0 → dosync-0.6.1}/tests/test_ha_bridge_hygiene.py +0 -0
  113. {dosync-0.5.0 → dosync-0.6.1}/tests/test_hub_monitor.py +0 -0
  114. {dosync-0.5.0 → dosync-0.6.1}/tests/test_idempotency.py +0 -0
  115. {dosync-0.5.0 → dosync-0.6.1}/tests/test_independent_observation.py +0 -0
  116. {dosync-0.5.0 → dosync-0.6.1}/tests/test_integration_suite.py +0 -0
  117. {dosync-0.5.0 → dosync-0.6.1}/tests/test_lightweight_heartbeat.py +0 -0
  118. {dosync-0.5.0 → dosync-0.6.1}/tests/test_mavlink_adapter.py +0 -0
  119. {dosync-0.5.0 → dosync-0.6.1}/tests/test_mavlink_channels.py +0 -0
  120. {dosync-0.5.0 → dosync-0.6.1}/tests/test_mavlink_listener.py +0 -0
  121. {dosync-0.5.0 → dosync-0.6.1}/tests/test_mavlink_return_home.py +0 -0
  122. {dosync-0.5.0 → dosync-0.6.1}/tests/test_mavlink_single_reader.py +0 -0
  123. {dosync-0.5.0 → dosync-0.6.1}/tests/test_mavlink_telemetry_closure.py +0 -0
  124. {dosync-0.5.0 → dosync-0.6.1}/tests/test_mcp_dynamic_intents.py +0 -0
  125. {dosync-0.5.0 → dosync-0.6.1}/tests/test_mcp_partial_progress.py +0 -0
  126. {dosync-0.5.0 → dosync-0.6.1}/tests/test_metrics.py +0 -0
  127. {dosync-0.5.0 → dosync-0.6.1}/tests/test_models.py +0 -0
  128. {dosync-0.5.0 → dosync-0.6.1}/tests/test_multihub_endpoints.py +0 -0
  129. {dosync-0.5.0 → dosync-0.6.1}/tests/test_notification_templates.py +0 -0
  130. {dosync-0.5.0 → dosync-0.6.1}/tests/test_operation_guards.py +0 -0
  131. {dosync-0.5.0 → dosync-0.6.1}/tests/test_operation_supervisor.py +0 -0
  132. {dosync-0.5.0 → dosync-0.6.1}/tests/test_operations.py +0 -0
  133. {dosync-0.5.0 → dosync-0.6.1}/tests/test_operations_endpoints.py +0 -0
  134. {dosync-0.5.0 → dosync-0.6.1}/tests/test_operations_persistence.py +0 -0
  135. {dosync-0.5.0 → dosync-0.6.1}/tests/test_operations_wiring.py +0 -0
  136. {dosync-0.5.0 → dosync-0.6.1}/tests/test_panel_polish_2026_07_21.py +0 -0
  137. {dosync-0.5.0 → dosync-0.6.1}/tests/test_policies.py +0 -0
  138. {dosync-0.5.0 → dosync-0.6.1}/tests/test_policy_config.py +0 -0
  139. {dosync-0.5.0 → dosync-0.6.1}/tests/test_public_claims.py +0 -0
  140. {dosync-0.5.0 → dosync-0.6.1}/tests/test_pushed_verification.py +0 -0
  141. {dosync-0.5.0 → dosync-0.6.1}/tests/test_quickstart_is_runnable.py +0 -0
  142. {dosync-0.5.0 → dosync-0.6.1}/tests/test_reachability_cause.py +0 -0
  143. {dosync-0.5.0 → dosync-0.6.1}/tests/test_recall_benchmark_postpolicy.py +0 -0
  144. {dosync-0.5.0 → dosync-0.6.1}/tests/test_reconciler.py +0 -0
  145. {dosync-0.5.0 → dosync-0.6.1}/tests/test_resolution_wiring.py +0 -0
  146. {dosync-0.5.0 → dosync-0.6.1}/tests/test_resolver_scoring.py +0 -0
  147. {dosync-0.5.0 → dosync-0.6.1}/tests/test_resolver_semantics.py +0 -0
  148. {dosync-0.5.0 → dosync-0.6.1}/tests/test_route_composer.py +0 -0
  149. {dosync-0.5.0 → dosync-0.6.1}/tests/test_sensor_kind.py +0 -0
  150. {dosync-0.5.0 → dosync-0.6.1}/tests/test_server.py +0 -0
  151. {dosync-0.5.0 → dosync-0.6.1}/tests/test_simulation_is_declared.py +0 -0
  152. {dosync-0.5.0 → dosync-0.6.1}/tests/test_telemetry_bridge.py +0 -0
  153. {dosync-0.5.0 → dosync-0.6.1}/tests/test_universal_intent_contract.py +0 -0
  154. {dosync-0.5.0 → dosync-0.6.1}/tests/test_validation.py +0 -0
  155. {dosync-0.5.0 → dosync-0.6.1}/tests/test_validation_integration.py +0 -0
  156. {dosync-0.5.0 → dosync-0.6.1}/tests/test_wiring_audit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dosync
3
- Version: 0.5.0
3
+ Version: 0.6.1
4
4
  Summary: The semantic layer between AI agents and physical devices
5
5
  Author-email: Rodrigo Giuliani <rgiuliani@dosync.dev>
6
6
  License-Expression: Apache-2.0
@@ -194,7 +194,7 @@ DoSync earns its place in specific situations — and honestly gets in the way i
194
194
  - Have a single device or one brand's ecosystem — you don't need a coordination layer.
195
195
  - Don't need auditability or a policy layer.
196
196
 
197
- **If the first list is you:** the fastest way in is the **[20-minute device tutorial](TUTORIAL.md)** — build a device that senses, expresses a goal, and acts, with the full audit trail. Or open an [issue](https://github.com/giulianireg-spec/dosync-protocol/issues) describing what you're trying to coordinate and we'll tell you honestly whether DoSync fits.
197
+ **If the first list is you:** the fastest way in is the **[20-minute device tutorial](docs/DEVICE-INTEGRATION.md)** — build a device that senses, expresses a goal, and acts, with the full audit trail. Or open an [issue](https://github.com/giulianireg-spec/dosync-protocol/issues) describing what you're trying to coordinate and we'll tell you honestly whether DoSync fits.
198
198
 
199
199
  ---
200
200
 
@@ -249,6 +249,20 @@ Benchmark (Raspberry Pi 5, Python 3.11.2):
249
249
 
250
250
  ---
251
251
 
252
+ ## Where to start
253
+
254
+ | You want to… | Go to |
255
+ |---|---|
256
+ | **run a hub and see your devices** | [Quick start](#quick-start), below — about five minutes, no terminal after the first command |
257
+ | **build a device that speaks the protocol** | [docs/DEVICE-INTEGRATION.md](docs/DEVICE-INTEGRATION.md) — needs Docker, a broker and Python |
258
+ | **implement DoSync yourself, or check conformance** | [spec/](spec/) — the wire format, the JSON schemas, and `certify.py` |
259
+ | **understand why it is built this way** | [DESIGN-PRINCIPLES.md](DESIGN-PRINCIPLES.md) |
260
+ | **know its scope, or who decides** | [docs/VISION.md](docs/VISION.md) · [GOVERNANCE.md](GOVERNANCE.md) |
261
+
262
+ Three of those four were reachable only by guessing. The one called `TUTORIAL.md`
263
+ sat in the repository root, where a reader looks first, and asked for Docker in
264
+ its first step.
265
+
252
266
  ## Quick start
253
267
 
254
268
  ### Install and run — five minutes, no hardware
@@ -283,6 +297,10 @@ Two things the rest of this page assumes that PowerShell does not provide:
283
297
  `curl.exe`, or the PowerShell examples further down. `setup_pki.sh` is a shell
284
298
  script and needs Git Bash or WSL.
285
299
 
300
+ If a control library for your hardware needs installing afterwards, a plain
301
+ `pip install` will not reach it — `pipx` keeps the hub in its own isolated
302
+ environment. See **pipx inject**, below.
303
+
286
304
  </details>
287
305
 
288
306
  ### Then open the dashboard
@@ -502,6 +520,22 @@ The operator runs `pip install dosync-adapter-daikin` and the hub finds it. No
502
520
  pull request here, and no promise from this project to maintain code for
503
521
  hardware it has never seen — the publisher answers for their own adapter.
504
522
 
523
+ **If DoSync itself was installed with `pipx`, a plain `pip install` lands in
524
+ the wrong environment.** `pipx` deliberately isolates the hub in its own
525
+ virtual environment, separate from the system Python, so a vendor library
526
+ installed the ordinary way is invisible to it — the hub stays silent about
527
+ this exactly as it does about an uninstalled one, because from its side
528
+ there is no difference. Install into the hub's own environment instead:
529
+
530
+ ```bash
531
+ pipx inject dosync dosync-adapter-daikin
532
+ ```
533
+
534
+ This is not specific to any one vendor's product: it is how you add *any*
535
+ optional dependency — a control library, a third-party adapter package, a
536
+ protocol SDK — after installing DoSync with `pipx`. Whichever hardware you
537
+ own, `pipx inject dosync <package>` is the command, not `pip install`.
538
+
505
539
  A third-party adapter runs inside the hub with the hub's permissions, so the hub
506
540
  says so: it is logged at WARNING when loaded, recorded in the audit chain, and
507
541
  reported as `kind: third_party` at `/v1/adapters` regardless of what the plugin
@@ -654,6 +688,104 @@ pytest # runs the full suite
654
688
  dosync-hub --reload
655
689
  ```
656
690
 
691
+ The hub stops when the terminal closes. To keep it running across reboots, see
692
+ [Keeping the hub running](#keeping-the-hub-running).
693
+
694
+ ---
695
+
696
+ ## Keeping the hub running
697
+
698
+ Everything above starts a hub in a terminal, and it stops when that terminal
699
+ closes. Nothing in this project has explained how to change that — while the
700
+ repository has shipped a systemd unit at its root for months and this page
701
+ refers to *"your service"* as though you already had one. That gap is the
702
+ reason this section exists.
703
+
704
+ DoSync does not implement supervision on any platform. It delegates: to systemd
705
+ on Linux, to the task scheduler on Windows. What differs between them is how
706
+ much you have to write, not whether it works.
707
+
708
+ **Say once what is at stake:** a hub that governs physical actions and does not
709
+ come back after a power cut is a deployment decision with consequences. If a
710
+ lock, an alarm or a machine depends on it, this is not optional.
711
+
712
+ ### Linux — systemd
713
+
714
+ `dosync.service` in the repository root is a template. Adjust the paths, then:
715
+
716
+ ```bash
717
+ sudo cp dosync.service /etc/systemd/system/
718
+ sudo systemctl daemon-reload
719
+ sudo systemctl enable --now dosync
720
+ systemctl status dosync
721
+ ```
722
+
723
+ **Keep secrets out of the unit.** It is a file people copy, and this one carried
724
+ a real Home Assistant token in a public repository until August 2026. Put them
725
+ in a drop-in that is not tracked:
726
+
727
+ ```bash
728
+ sudo install -d -m 700 /etc/systemd/system/dosync.service.d
729
+ sudo tee /etc/systemd/system/dosync.service.d/local.conf >/dev/null <<'EOF'
730
+ [Service]
731
+ Environment="HA_TOKEN=your-token-here"
732
+ EOF
733
+ sudo chmod 600 /etc/systemd/system/dosync.service.d/local.conf
734
+ sudo systemctl daemon-reload && sudo systemctl restart dosync
735
+ ```
736
+
737
+ systemd merges drop-ins over the unit, so the template never needs editing.
738
+
739
+ ### Windows — Task Scheduler
740
+
741
+ *Verified on Windows 11 ARM64 in a virtual machine, Python 3.14, installed with
742
+ `pipx`.*
743
+
744
+ Run PowerShell **as administrator**:
745
+
746
+ ```powershell
747
+ $exe = (Get-Command dosync-hub).Source
748
+ $db = "$env:USERPROFILE\.local\state\dosync\dosync.db"
749
+
750
+ $action = New-ScheduledTaskAction -Execute "cmd.exe" `
751
+ -Argument "/c set DOSYNC_DB=$db && `"$exe`""
752
+ $trigger = New-ScheduledTaskTrigger -AtStartup
753
+ $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" `
754
+ -LogonType ServiceAccount -RunLevel Highest
755
+ $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries `
756
+ -DontStopIfGoingOnBatteries -RestartCount 3 `
757
+ -RestartInterval (New-TimeSpan -Minutes 1)
758
+
759
+ Register-ScheduledTask -TaskName "DoSync Hub" -Action $action `
760
+ -Trigger $trigger -Principal $principal -Settings $settings
761
+ ```
762
+
763
+ `-AtStartup` with `SYSTEM` means the hub comes back after a reboot without
764
+ anyone logging in — the closest equivalent to systemd. `-RestartCount` covers a
765
+ process that dies.
766
+
767
+ **`DOSYNC_DB` is not optional, and leaving it out fails silently.** A scheduled
768
+ task does not inherit your environment, so a hub started this way writes to
769
+ `C:\Windows\System32\config\systemprofile\` — a different database from the
770
+ one you used by hand. It starts perfectly, reports `devices: 0`, and looks like
771
+ your inventory was lost. Setting `DOSYNC_DB` points both at the same file;
772
+ running it by hand afterwards reads what the scheduled task wrote, with no
773
+ permission conflict.
774
+
775
+ Check it:
776
+
777
+ ```powershell
778
+ curl.exe -s http://localhost:47200/v1/status | findstr db_path
779
+ Get-ScheduledTask -TaskName "DoSync Hub" | Get-ScheduledTaskInfo
780
+ ```
781
+
782
+ `LastTaskResult: 267009` is `0x41301` — *the task is currently running*. That is
783
+ the healthy value here; `0` would mean it exited.
784
+
785
+ **NSSM** installs the hub as a real Windows service and is what a server
786
+ deployment would use. It is third-party software this project does not ship or
787
+ test, so it is named rather than recommended.
788
+
657
789
  ---
658
790
 
659
791
  ## What's built today
@@ -726,7 +858,7 @@ is implementable in a second language against the same certification suite —
726
858
  both share the same author. A genuinely **independent** implementation
727
859
  (different author or organization) is a tracked milestone for v1.0: a protocol
728
860
  needs multiple independent implementations to become a standard. See the
729
- [roadmap](ROADMAP.md).
861
+ [vision and scope](docs/VISION.md).
730
862
 
731
863
  ---
732
864
 
@@ -757,7 +889,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow, including the C
757
889
  - [spec/DoSync-SPEC-v0.1.md](spec/DoSync-SPEC-v0.1.md) — full protocol specification
758
890
  - [spec/RESOLVER-SPEC-v0.3.md](spec/RESOLVER-SPEC-v0.3.md) — resolver interface + external resolver protocol
759
891
  - [DESIGN-PRINCIPLES.md](DESIGN-PRINCIPLES.md) — architectural decisions and rationale
760
- - [COMPATIBILITY.md](COMPATIBILITY.md) — backward compatibility guarantees
892
+ - [COMPATIBILITY.md](docs/COMPATIBILITY.md) — backward compatibility guarantees
761
893
 
762
894
  ---
763
895
 
@@ -132,7 +132,7 @@ DoSync earns its place in specific situations — and honestly gets in the way i
132
132
  - Have a single device or one brand's ecosystem — you don't need a coordination layer.
133
133
  - Don't need auditability or a policy layer.
134
134
 
135
- **If the first list is you:** the fastest way in is the **[20-minute device tutorial](TUTORIAL.md)** — build a device that senses, expresses a goal, and acts, with the full audit trail. Or open an [issue](https://github.com/giulianireg-spec/dosync-protocol/issues) describing what you're trying to coordinate and we'll tell you honestly whether DoSync fits.
135
+ **If the first list is you:** the fastest way in is the **[20-minute device tutorial](docs/DEVICE-INTEGRATION.md)** — build a device that senses, expresses a goal, and acts, with the full audit trail. Or open an [issue](https://github.com/giulianireg-spec/dosync-protocol/issues) describing what you're trying to coordinate and we'll tell you honestly whether DoSync fits.
136
136
 
137
137
  ---
138
138
 
@@ -187,6 +187,20 @@ Benchmark (Raspberry Pi 5, Python 3.11.2):
187
187
 
188
188
  ---
189
189
 
190
+ ## Where to start
191
+
192
+ | You want to… | Go to |
193
+ |---|---|
194
+ | **run a hub and see your devices** | [Quick start](#quick-start), below — about five minutes, no terminal after the first command |
195
+ | **build a device that speaks the protocol** | [docs/DEVICE-INTEGRATION.md](docs/DEVICE-INTEGRATION.md) — needs Docker, a broker and Python |
196
+ | **implement DoSync yourself, or check conformance** | [spec/](spec/) — the wire format, the JSON schemas, and `certify.py` |
197
+ | **understand why it is built this way** | [DESIGN-PRINCIPLES.md](DESIGN-PRINCIPLES.md) |
198
+ | **know its scope, or who decides** | [docs/VISION.md](docs/VISION.md) · [GOVERNANCE.md](GOVERNANCE.md) |
199
+
200
+ Three of those four were reachable only by guessing. The one called `TUTORIAL.md`
201
+ sat in the repository root, where a reader looks first, and asked for Docker in
202
+ its first step.
203
+
190
204
  ## Quick start
191
205
 
192
206
  ### Install and run — five minutes, no hardware
@@ -221,6 +235,10 @@ Two things the rest of this page assumes that PowerShell does not provide:
221
235
  `curl.exe`, or the PowerShell examples further down. `setup_pki.sh` is a shell
222
236
  script and needs Git Bash or WSL.
223
237
 
238
+ If a control library for your hardware needs installing afterwards, a plain
239
+ `pip install` will not reach it — `pipx` keeps the hub in its own isolated
240
+ environment. See **pipx inject**, below.
241
+
224
242
  </details>
225
243
 
226
244
  ### Then open the dashboard
@@ -440,6 +458,22 @@ The operator runs `pip install dosync-adapter-daikin` and the hub finds it. No
440
458
  pull request here, and no promise from this project to maintain code for
441
459
  hardware it has never seen — the publisher answers for their own adapter.
442
460
 
461
+ **If DoSync itself was installed with `pipx`, a plain `pip install` lands in
462
+ the wrong environment.** `pipx` deliberately isolates the hub in its own
463
+ virtual environment, separate from the system Python, so a vendor library
464
+ installed the ordinary way is invisible to it — the hub stays silent about
465
+ this exactly as it does about an uninstalled one, because from its side
466
+ there is no difference. Install into the hub's own environment instead:
467
+
468
+ ```bash
469
+ pipx inject dosync dosync-adapter-daikin
470
+ ```
471
+
472
+ This is not specific to any one vendor's product: it is how you add *any*
473
+ optional dependency — a control library, a third-party adapter package, a
474
+ protocol SDK — after installing DoSync with `pipx`. Whichever hardware you
475
+ own, `pipx inject dosync <package>` is the command, not `pip install`.
476
+
443
477
  A third-party adapter runs inside the hub with the hub's permissions, so the hub
444
478
  says so: it is logged at WARNING when loaded, recorded in the audit chain, and
445
479
  reported as `kind: third_party` at `/v1/adapters` regardless of what the plugin
@@ -592,6 +626,104 @@ pytest # runs the full suite
592
626
  dosync-hub --reload
593
627
  ```
594
628
 
629
+ The hub stops when the terminal closes. To keep it running across reboots, see
630
+ [Keeping the hub running](#keeping-the-hub-running).
631
+
632
+ ---
633
+
634
+ ## Keeping the hub running
635
+
636
+ Everything above starts a hub in a terminal, and it stops when that terminal
637
+ closes. Nothing in this project has explained how to change that — while the
638
+ repository has shipped a systemd unit at its root for months and this page
639
+ refers to *"your service"* as though you already had one. That gap is the
640
+ reason this section exists.
641
+
642
+ DoSync does not implement supervision on any platform. It delegates: to systemd
643
+ on Linux, to the task scheduler on Windows. What differs between them is how
644
+ much you have to write, not whether it works.
645
+
646
+ **Say once what is at stake:** a hub that governs physical actions and does not
647
+ come back after a power cut is a deployment decision with consequences. If a
648
+ lock, an alarm or a machine depends on it, this is not optional.
649
+
650
+ ### Linux — systemd
651
+
652
+ `dosync.service` in the repository root is a template. Adjust the paths, then:
653
+
654
+ ```bash
655
+ sudo cp dosync.service /etc/systemd/system/
656
+ sudo systemctl daemon-reload
657
+ sudo systemctl enable --now dosync
658
+ systemctl status dosync
659
+ ```
660
+
661
+ **Keep secrets out of the unit.** It is a file people copy, and this one carried
662
+ a real Home Assistant token in a public repository until August 2026. Put them
663
+ in a drop-in that is not tracked:
664
+
665
+ ```bash
666
+ sudo install -d -m 700 /etc/systemd/system/dosync.service.d
667
+ sudo tee /etc/systemd/system/dosync.service.d/local.conf >/dev/null <<'EOF'
668
+ [Service]
669
+ Environment="HA_TOKEN=your-token-here"
670
+ EOF
671
+ sudo chmod 600 /etc/systemd/system/dosync.service.d/local.conf
672
+ sudo systemctl daemon-reload && sudo systemctl restart dosync
673
+ ```
674
+
675
+ systemd merges drop-ins over the unit, so the template never needs editing.
676
+
677
+ ### Windows — Task Scheduler
678
+
679
+ *Verified on Windows 11 ARM64 in a virtual machine, Python 3.14, installed with
680
+ `pipx`.*
681
+
682
+ Run PowerShell **as administrator**:
683
+
684
+ ```powershell
685
+ $exe = (Get-Command dosync-hub).Source
686
+ $db = "$env:USERPROFILE\.local\state\dosync\dosync.db"
687
+
688
+ $action = New-ScheduledTaskAction -Execute "cmd.exe" `
689
+ -Argument "/c set DOSYNC_DB=$db && `"$exe`""
690
+ $trigger = New-ScheduledTaskTrigger -AtStartup
691
+ $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" `
692
+ -LogonType ServiceAccount -RunLevel Highest
693
+ $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries `
694
+ -DontStopIfGoingOnBatteries -RestartCount 3 `
695
+ -RestartInterval (New-TimeSpan -Minutes 1)
696
+
697
+ Register-ScheduledTask -TaskName "DoSync Hub" -Action $action `
698
+ -Trigger $trigger -Principal $principal -Settings $settings
699
+ ```
700
+
701
+ `-AtStartup` with `SYSTEM` means the hub comes back after a reboot without
702
+ anyone logging in — the closest equivalent to systemd. `-RestartCount` covers a
703
+ process that dies.
704
+
705
+ **`DOSYNC_DB` is not optional, and leaving it out fails silently.** A scheduled
706
+ task does not inherit your environment, so a hub started this way writes to
707
+ `C:\Windows\System32\config\systemprofile\` — a different database from the
708
+ one you used by hand. It starts perfectly, reports `devices: 0`, and looks like
709
+ your inventory was lost. Setting `DOSYNC_DB` points both at the same file;
710
+ running it by hand afterwards reads what the scheduled task wrote, with no
711
+ permission conflict.
712
+
713
+ Check it:
714
+
715
+ ```powershell
716
+ curl.exe -s http://localhost:47200/v1/status | findstr db_path
717
+ Get-ScheduledTask -TaskName "DoSync Hub" | Get-ScheduledTaskInfo
718
+ ```
719
+
720
+ `LastTaskResult: 267009` is `0x41301` — *the task is currently running*. That is
721
+ the healthy value here; `0` would mean it exited.
722
+
723
+ **NSSM** installs the hub as a real Windows service and is what a server
724
+ deployment would use. It is third-party software this project does not ship or
725
+ test, so it is named rather than recommended.
726
+
595
727
  ---
596
728
 
597
729
  ## What's built today
@@ -664,7 +796,7 @@ is implementable in a second language against the same certification suite —
664
796
  both share the same author. A genuinely **independent** implementation
665
797
  (different author or organization) is a tracked milestone for v1.0: a protocol
666
798
  needs multiple independent implementations to become a standard. See the
667
- [roadmap](ROADMAP.md).
799
+ [vision and scope](docs/VISION.md).
668
800
 
669
801
  ---
670
802
 
@@ -695,7 +827,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow, including the C
695
827
  - [spec/DoSync-SPEC-v0.1.md](spec/DoSync-SPEC-v0.1.md) — full protocol specification
696
828
  - [spec/RESOLVER-SPEC-v0.3.md](spec/RESOLVER-SPEC-v0.3.md) — resolver interface + external resolver protocol
697
829
  - [DESIGN-PRINCIPLES.md](DESIGN-PRINCIPLES.md) — architectural decisions and rationale
698
- - [COMPATIBILITY.md](COMPATIBILITY.md) — backward compatibility guarantees
830
+ - [COMPATIBILITY.md](docs/COMPATIBILITY.md) — backward compatibility guarantees
699
831
 
700
832
  ---
701
833
 
@@ -13,5 +13,5 @@ The two numbers move independently on purpose:
13
13
  __version__ this implementation of the hub (semver)
14
14
  __protocol_version__ the wire contract other implementations must match
15
15
  """
16
- __version__ = "0.5.0"
16
+ __version__ = "0.6.1"
17
17
  __protocol_version__ = "0.4"