tensorfs 0.3.74__tar.gz → 0.3.75__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 (170) hide show
  1. {tensorfs-0.3.74 → tensorfs-0.3.75}/Cargo.lock +2 -2
  2. {tensorfs-0.3.74 → tensorfs-0.3.75}/Cargo.toml +1 -1
  3. {tensorfs-0.3.74 → tensorfs-0.3.75}/PKG-INFO +1 -1
  4. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/bin/tfs/main.rs +13 -1
  5. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/err.rs +1 -0
  6. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/http.rs +51 -9
  7. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/mod.rs +1 -1
  8. tensorfs-0.3.75/crates/tensorfs-core/tests/hub_tls.rs +124 -0
  9. {tensorfs-0.3.74 → tensorfs-0.3.75}/pyproject.toml +1 -1
  10. {tensorfs-0.3.74 → tensorfs-0.3.75}/python/tensorfs/_ext.pyi +1 -0
  11. {tensorfs-0.3.74 → tensorfs-0.3.75}/python/tensorfs/errors.py +2 -0
  12. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/Cargo.toml +0 -0
  13. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/build.rs +0 -0
  14. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
  15. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
  16. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
  17. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
  18. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/asset_update.rs +0 -0
  19. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/b64.rs +0 -0
  20. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
  21. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
  22. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/bin/tfs/fetch.rs +0 -0
  23. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
  24. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/bin/tfs/rd.rs +0 -0
  25. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/bin/tfs/repo.rs +0 -0
  26. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/bin/tfs/snap.rs +0 -0
  27. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
  28. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/cache_roots.rs +0 -0
  29. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/canon.rs +0 -0
  30. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/capability.rs +0 -0
  31. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/catalog/lock.rs +0 -0
  32. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/catalog.rs +0 -0
  33. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/cbor.rs +0 -0
  34. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/checkpoint.rs +0 -0
  35. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/checkpoint_root.rs +0 -0
  36. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/corpus.rs +0 -0
  37. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/adoption.rs +0 -0
  38. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/companion_tests.rs +0 -0
  39. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/estimate.rs +0 -0
  40. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
  41. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/group_commit_tests.rs +0 -0
  42. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/history_cost_tests.rs +0 -0
  43. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/part_rows_tests.rs +0 -0
  44. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/progress.rs +0 -0
  45. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/resume_tests.rs +0 -0
  46. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/retention.rs +0 -0
  47. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived/roots.rs +0 -0
  48. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/derived.rs +0 -0
  49. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/descriptors.rs +0 -0
  50. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/disk.rs +0 -0
  51. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/dtype.rs +0 -0
  52. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/durability.rs +0 -0
  53. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/fetch.rs +0 -0
  54. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/fill.rs +0 -0
  55. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/fit.rs +0 -0
  56. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/gc.rs +0 -0
  57. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/header.rs +0 -0
  58. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/home.rs +0 -0
  59. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ids.rs +0 -0
  60. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
  61. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
  62. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/custody.rs +0 -0
  63. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
  64. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
  65. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/h3_lora.rs +0 -0
  66. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
  67. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
  68. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/narrow.rs +0 -0
  69. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/preflight.rs +0 -0
  70. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/routes/sdxl.routes.json +0 -0
  71. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/routes/sdxl.spec.json +0 -0
  72. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/routes.rs +0 -0
  73. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/source.rs +0 -0
  74. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
  75. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/ingest/transaction.rs +0 -0
  76. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/jcs.rs +0 -0
  77. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/lib.rs +0 -0
  78. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/limits.rs +0 -0
  79. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/machine.rs +0 -0
  80. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/manifest.rs +0 -0
  81. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/meta.rs +0 -0
  82. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/project.rs +0 -0
  83. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/providers.rs +0 -0
  84. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/read.rs +0 -0
  85. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/receipt.rs +0 -0
  86. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/reclaim.rs +0 -0
  87. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/registry.rs +0 -0
  88. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/relation.rs +0 -0
  89. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/repo_cache/identity.rs +0 -0
  90. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/repo_cache.rs +0 -0
  91. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/repository.rs +0 -0
  92. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/sha256.rs +0 -0
  93. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/source_artifact.rs +0 -0
  94. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/spec.rs +0 -0
  95. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/staging.rs +0 -0
  96. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/stats.rs +0 -0
  97. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/storage.rs +0 -0
  98. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/store.rs +0 -0
  99. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/api.rs +0 -0
  100. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/http_reuse_tests.rs +0 -0
  101. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/hub.rs +0 -0
  102. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/ledger.rs +0 -0
  103. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/memory.rs +0 -0
  104. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/pace.rs +0 -0
  105. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/policy.rs +0 -0
  106. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
  107. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/pull.rs +0 -0
  108. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/push.rs +0 -0
  109. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
  110. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/speculate.rs +0 -0
  111. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/transport/tests.rs +0 -0
  112. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/unsynced.rs +0 -0
  113. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/src/vectors.rs +0 -0
  114. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/additive_fields.rs +0 -0
  115. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/anima_hf_profile.rs +0 -0
  116. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/as_is.rs +0 -0
  117. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/checkpoint_roots.rs +0 -0
  118. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/civitai_sdxl_profile.rs +0 -0
  119. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fd_budget.rs +0 -0
  120. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fd_headroom.rs +0 -0
  121. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
  122. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fit.rs +0 -0
  123. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
  124. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
  125. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
  126. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
  127. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
  128. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
  129. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
  130. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/foreign_json.rs +0 -0
  131. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/h3_lora.rs +0 -0
  132. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
  133. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/home.rs +0 -0
  134. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/import_tree.rs +0 -0
  135. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
  136. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/liveness.rs +0 -0
  137. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/qwen_image21_profile.rs +0 -0
  138. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/read_plan.rs +0 -0
  139. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/reclaim.rs +0 -0
  140. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/reclaim_run.rs +0 -0
  141. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/repo_cache.rs +0 -0
  142. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/repo_store.rs +0 -0
  143. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
  144. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/routed_single_file.rs +0 -0
  145. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
  146. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
  147. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
  148. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/source_download.rs +0 -0
  149. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/source_plan.rs +0 -0
  150. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
  151. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
  152. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/staging.rs +0 -0
  153. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
  154. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
  155. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/transfer_memory.rs +0 -0
  156. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/trust_index.rs +0 -0
  157. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/version.rs +0 -0
  158. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-core/tests/write_through.rs +0 -0
  159. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-py/Cargo.toml +0 -0
  160. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-py/src/docs.rs +0 -0
  161. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-py/src/errors.rs +0 -0
  162. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-py/src/ingest.rs +0 -0
  163. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-py/src/lib.rs +0 -0
  164. {tensorfs-0.3.74 → tensorfs-0.3.75}/crates/tensorfs-py/src/store.rs +0 -0
  165. {tensorfs-0.3.74 → tensorfs-0.3.75}/python/tensorfs/__init__.py +0 -0
  166. {tensorfs-0.3.74 → tensorfs-0.3.75}/python/tensorfs/_derived_channel.py +0 -0
  167. {tensorfs-0.3.74 → tensorfs-0.3.75}/python/tensorfs/_source_channel.py +0 -0
  168. {tensorfs-0.3.74 → tensorfs-0.3.75}/python/tensorfs/_tensors.py +0 -0
  169. {tensorfs-0.3.74 → tensorfs-0.3.75}/python/tensorfs/derived.py +0 -0
  170. {tensorfs-0.3.74 → tensorfs-0.3.75}/python/tensorfs/py.typed +0 -0
@@ -439,7 +439,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
439
439
 
440
440
  [[package]]
441
441
  name = "tensorfs-core"
442
- version = "0.3.74"
442
+ version = "0.3.75"
443
443
  dependencies = [
444
444
  "fs2",
445
445
  "libc",
@@ -454,7 +454,7 @@ dependencies = [
454
454
 
455
455
  [[package]]
456
456
  name = "tensorfs-py"
457
- version = "0.3.74"
457
+ version = "0.3.75"
458
458
  dependencies = [
459
459
  "pyo3",
460
460
  "tensorfs-core",
@@ -3,5 +3,5 @@ resolver = "2"
3
3
  members = ["crates/tensorfs-core", "crates/tensorfs-py"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.3.74"
6
+ version = "0.3.75"
7
7
  edition = "2021"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tensorfs
3
- Version: 0.3.74
3
+ Version: 0.3.75
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Rust
@@ -41,7 +41,8 @@
41
41
  //! The ONE transport (tfs-048/tfs-049/tfs-050). The credential is `--credential-file` or
42
42
  //! TFS_CREDENTIAL (`bearer <token>`, `worker <id> <token>`, or empty for anonymous), never
43
43
  //! bare argv; a URL of `-` arrives on stdin. Flags: --hub <base> --lane --session --streams <n>
44
- //! --streams-start <n> --timeout <s> --allow-hosts a,b,.c --allow-local --max-redirects <n>.
44
+ //! --streams-start <n> --timeout <s> --allow-hosts a,b,.c --allow-local --max-redirects <n>
45
+ //! --ca-file <pem> (a private hub's CA, trusted beside the webpki set).
45
46
  //! `--streams` is the most objects in flight (default 128, memory-bounded); the walk starts
46
47
  //! at `--streams-start` (default 16) and doubles while measured goodput follows.
47
48
  //! `tfs fetch` reads and backfills the Store's bound repo cache between the local Store
@@ -202,6 +203,17 @@ fn accept_printed_ids(args: Vec<String>) -> Vec<String> {
202
203
 
203
204
  fn main() -> ExitCode {
204
205
  let (args, flags) = split_flags(std::env::args().skip(1).collect());
206
+ if let Some(path) = flag(&flags, "ca-file") {
207
+ let trusted = fs::read(path)
208
+ .map_err(|e| format!("IO_FAILED: read {path}: {e}"))
209
+ .and_then(|pem| {
210
+ tensorfs_core::transport::trust_roots(&pem).map_err(|r| format!("{r} ({path})"))
211
+ });
212
+ if let Err(refusal) = trusted {
213
+ eprintln!("REFUSED {refusal}");
214
+ return ExitCode::FAILURE;
215
+ }
216
+ }
205
217
  let args = accept_printed_ids(args);
206
218
  let a: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
207
219
  match a.as_slice() {
@@ -189,6 +189,7 @@ codes![
189
189
  REDIRECT_REFUSED,
190
190
  HUB_UNREACHABLE,
191
191
  HUB_REFUSED,
192
+ TLS_UNTRUSTED,
192
193
  REF_NOT_FOUND,
193
194
  CREDENTIAL_REQUIRED,
194
195
  TRANSFER_FAILED,
@@ -30,10 +30,11 @@
30
30
  use crate::err::{refuse, Code, Refusal, Result};
31
31
  use crate::transport::ledger::{Deadline, Ledger};
32
32
  use crate::transport::policy::CheckedUrl;
33
+ use rustls::pki_types::pem::PemObject;
33
34
  use rustls::pki_types::{CertificateDer, ServerName};
34
35
  use std::io::{ErrorKind, Read, Write};
35
36
  use std::net::TcpStream;
36
- use std::sync::{Arc, Mutex};
37
+ use std::sync::{Arc, Mutex, OnceLock};
37
38
  use std::time::{Duration, Instant};
38
39
 
39
40
  const HEAD_MAX_BYTES: usize = 64 * 1024;
@@ -211,13 +212,35 @@ struct Idle {
211
212
  wire: Wire,
212
213
  }
213
214
 
215
+ /// Authorities this process trusts beside the webpki set: a private hub's CA, named by its
216
+ /// caller as a location (`tfs --ca-file`). Set once, before the first exchange.
217
+ static EXTRA_ROOTS: OnceLock<Vec<CertificateDer<'static>>> = OnceLock::new();
218
+
219
+ /// Trust the PEM certificates in `pem` for every client this process builds.
220
+ pub fn trust_roots(pem: &[u8]) -> Result<usize> {
221
+ let roots: Vec<_> = CertificateDer::pem_slice_iter(pem)
222
+ .collect::<std::result::Result<_, _>>()
223
+ .map_err(|e| Refusal {
224
+ code: Code::UNKNOWN_FORMAT,
225
+ detail: format!("not a PEM certificate bundle: {e}"),
226
+ })?;
227
+ let (usable, _) = rustls::RootCertStore::empty().add_parsable_certificates(roots.clone());
228
+ if roots.is_empty() || usable != roots.len() {
229
+ return refuse(Code::UNKNOWN_FORMAT, "holds no usable CA certificate");
230
+ }
231
+ EXTRA_ROOTS.set(roots).map_err(|_| Refusal {
232
+ code: Code::UNKNOWN_FORMAT,
233
+ detail: "this process already chose its trust roots".into(),
234
+ })?;
235
+ Ok(usable)
236
+ }
237
+
214
238
  impl Client {
215
239
  pub fn new() -> Client {
216
- Client::with_extra_roots(&[])
240
+ Client::with_extra_roots(EXTRA_ROOTS.get().map_or(&[], Vec::as_slice))
217
241
  }
218
242
 
219
- /// `extra_roots` exists for tests that stand up a loopback TLS origin under a
220
- /// self-signed authority; production callers pass none and trust the webpki set.
243
+ /// Trusts the webpki set plus `extra_roots`.
221
244
  pub fn with_extra_roots(extra_roots: &[CertificateDer<'static>]) -> Client {
222
245
  let mut roots = rustls::RootCertStore::empty();
223
246
  roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
@@ -482,7 +505,9 @@ fn connect(
482
505
  )
483
506
  }
484
507
 
508
+ #[allow(clippy::too_many_arguments)]
485
509
  fn tls_handshake(
510
+ host: &str,
486
511
  sock: &mut TcpStream,
487
512
  conn: &mut rustls::ClientConnection,
488
513
  deadline: Deadline,
@@ -516,10 +541,18 @@ fn tls_handshake(
516
541
  Ok(0) => return refuse(fail, "origin closed the connection mid-handshake"),
517
542
  Ok(_) => {
518
543
  advanced = Instant::now();
519
- conn.process_new_packets().map_err(|e| Refusal {
520
- code: fail,
521
- detail: format!("TLS refused: {e}"),
522
- })?;
544
+ // A certificate or protocol verdict is this client's, and re-asking reaches
545
+ // it again: refused typed, never retried as weather. The alert goes out so
546
+ // the origin logs why.
547
+ if let Err(e) = conn.process_new_packets() {
548
+ let _ = conn.write_tls(sock);
549
+ return refuse(
550
+ Code::TLS_UNTRUSTED,
551
+ format!(
552
+ "{host}: TLS refused: {e}; a private hub names its CA with --ca-file"
553
+ ),
554
+ );
555
+ }
523
556
  }
524
557
  Err(e) if would_block(&e) => {
525
558
  stream_verdict(judge, ledger, advanced, "TLS handshake")?;
@@ -1041,7 +1074,16 @@ pub fn send<'l>(
1041
1074
  })?;
1042
1075
  let mut sock = sock;
1043
1076
  let mut conn = Box::new(conn);
1044
- tls_handshake(&mut sock, &mut conn, deadline, ledger, judge, opened, fail)?;
1077
+ tls_handshake(
1078
+ &checked.host,
1079
+ &mut sock,
1080
+ &mut conn,
1081
+ deadline,
1082
+ ledger,
1083
+ judge,
1084
+ opened,
1085
+ fail,
1086
+ )?;
1045
1087
  Wire::Tls { sock, conn }
1046
1088
  } else {
1047
1089
  Wire::Plain(sock)
@@ -49,7 +49,7 @@ mod speculate;
49
49
  mod tests;
50
50
 
51
51
  pub use api::{api_get, fetch_prefix, probe_length, Prefix};
52
- pub use http::Client;
52
+ pub use http::{trust_roots, Client};
53
53
  pub use hub::{
54
54
  closure, closure_all, credential_from_spec, presign, Anonymous, Closure, CredentialProvider,
55
55
  HostToken, Presigned, ScopedHeaders,
@@ -0,0 +1,124 @@
1
+ //! A hub whose certificate this machine does not trust is a verdict, not weather: `tfs fetch`
2
+ //! refuses it typed on the first handshake, and `--ca-file` names the private CA that makes
3
+ //! the same hub reachable.
4
+
5
+ use std::io::{Read, Write};
6
+ use std::net::{SocketAddr, TcpListener, TcpStream};
7
+ use std::process::Command;
8
+ use std::sync::{mpsc, Arc};
9
+
10
+ const ENVELOPE: &str =
11
+ r#"{"error":{"code":"checkpoint_not_found","message":"no such checkpoint"}}"#;
12
+
13
+ /// A loopback hub under a self-signed authority that answers every call it can read with a
14
+ /// typed 404. Every accepted peer is reported before its handshake.
15
+ fn private_hub() -> (u16, String, mpsc::Receiver<SocketAddr>) {
16
+ let key = rcgen::generate_simple_self_signed(vec!["127.0.0.1".into()]).unwrap();
17
+ let cert = rustls::pki_types::CertificateDer::from(key.cert.der().to_vec());
18
+ let private = rustls::pki_types::PrivateKeyDer::try_from(key.key_pair.serialize_der())
19
+ .unwrap()
20
+ .clone_key();
21
+ let config = rustls::ServerConfig::builder_with_provider(Arc::new(
22
+ rustls::crypto::ring::default_provider(),
23
+ ))
24
+ .with_safe_default_protocol_versions()
25
+ .unwrap()
26
+ .with_no_client_auth()
27
+ .with_single_cert(vec![cert], private)
28
+ .unwrap();
29
+ let config = Arc::new(config);
30
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
31
+ let port = listener.local_addr().unwrap().port();
32
+ let (peers, accepted) = mpsc::channel();
33
+ std::thread::spawn(move || {
34
+ for socket in listener.incoming() {
35
+ let Ok(socket) = socket else { return };
36
+ let _ = peers.send(socket.peer_addr().unwrap());
37
+ let conn = rustls::ServerConnection::new(Arc::clone(&config)).unwrap();
38
+ let mut stream = rustls::StreamOwned::new(conn, socket);
39
+ let mut head = Vec::new();
40
+ let mut chunk = [0u8; 4096];
41
+ while !head.windows(4).any(|w| w == b"\r\n\r\n") {
42
+ match stream.read(&mut chunk) {
43
+ Ok(n) if n > 0 => head.extend_from_slice(&chunk[..n]),
44
+ _ => break,
45
+ }
46
+ }
47
+ if head.windows(4).any(|w| w == b"\r\n\r\n") {
48
+ let answer = format!(
49
+ "HTTP/1.1 404 Not Found\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{ENVELOPE}",
50
+ ENVELOPE.len()
51
+ );
52
+ let _ = stream.write_all(answer.as_bytes());
53
+ stream.conn.send_close_notify();
54
+ let _ = stream.flush();
55
+ }
56
+ }
57
+ });
58
+ (port, key.cert.pem(), accepted)
59
+ }
60
+
61
+ /// How many connections reached the hub before this call: a sentinel connection is accepted
62
+ /// after every one queued ahead of it.
63
+ fn connections(port: u16, accepted: &mpsc::Receiver<SocketAddr>) -> usize {
64
+ let sentinel = TcpStream::connect(("127.0.0.1", port)).unwrap();
65
+ let mine = sentinel.local_addr().unwrap();
66
+ accepted.iter().take_while(|peer| *peer != mine).count()
67
+ }
68
+
69
+ fn fetch(root: &std::path::Path, port: u16, extra: &[&str]) -> (bool, String) {
70
+ let out = Command::new(env!("CARGO_BIN_EXE_tfs"))
71
+ .args(["fetch", root.to_str().unwrap(), "proof/model"])
72
+ .args(["--hub", &format!("https://127.0.0.1:{port}")])
73
+ .args(extra)
74
+ .env("TFS_CREDENTIAL", "")
75
+ .output()
76
+ .unwrap();
77
+ (
78
+ out.status.success(),
79
+ String::from_utf8_lossy(&out.stderr).into_owned(),
80
+ )
81
+ }
82
+
83
+ #[test]
84
+ fn an_untrusted_hub_is_refused_once_and_a_named_ca_reaches_it() {
85
+ let dir = std::env::temp_dir().join(format!("tfs-hub-tls-{}", std::process::id()));
86
+ let _ = std::fs::remove_dir_all(&dir);
87
+ std::fs::create_dir_all(&dir).unwrap();
88
+ let root = dir.join("store");
89
+ let init = Command::new(env!("CARGO_BIN_EXE_tfs"))
90
+ .args(["store", "init", root.to_str().unwrap()])
91
+ .output()
92
+ .unwrap();
93
+ assert!(
94
+ init.status.success(),
95
+ "{}",
96
+ String::from_utf8_lossy(&init.stderr)
97
+ );
98
+ let (port, pem, accepted) = private_hub();
99
+
100
+ let (ok, stderr) = fetch(&root, port, &[]);
101
+ assert!(
102
+ !ok && stderr.contains("REFUSED TLS_UNTRUSTED") && stderr.contains("127.0.0.1"),
103
+ "{stderr}"
104
+ );
105
+ assert_eq!(
106
+ connections(port, &accepted),
107
+ 1,
108
+ "an untrusted hub was asked again: {stderr}"
109
+ );
110
+
111
+ let ca = dir.join("hub-ca.pem");
112
+ std::fs::write(&ca, pem).unwrap();
113
+ let (ok, stderr) = fetch(&root, port, &["--ca-file", ca.to_str().unwrap()]);
114
+ assert!(
115
+ !ok && stderr.contains("REFUSED REF_NOT_FOUND") && stderr.contains("checkpoint_not_found"),
116
+ "{stderr}"
117
+ );
118
+
119
+ let garbage = dir.join("garbage.pem");
120
+ std::fs::write(&garbage, "not a certificate").unwrap();
121
+ let (ok, stderr) = fetch(&root, port, &["--ca-file", garbage.to_str().unwrap()]);
122
+ assert!(!ok && stderr.contains("REFUSED UNKNOWN_FORMAT"), "{stderr}");
123
+ let _ = std::fs::remove_dir_all(&dir);
124
+ }
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "tensorfs"
7
- version = "0.3.74"
7
+ version = "0.3.75"
8
8
  description = "TensorFS — canonical tensor artifact substrate"
9
9
  requires-python = ">=3.12"
10
10
  classifiers = [
@@ -278,6 +278,7 @@ class StoreEra(Refusal): ...
278
278
  class StoreRootAbsent(Refusal): ...
279
279
  class SweepSetIncomplete(Refusal): ...
280
280
  class TensorSchemaMismatch(Refusal): ...
281
+ class TlsUntrusted(Refusal): ...
281
282
  class TotalBytesCap(Refusal): ...
282
283
  class TotalRefsCap(Refusal): ...
283
284
  class TrailingBytes(Refusal): ...
@@ -149,6 +149,7 @@ from ._ext import (
149
149
  StoreRootAbsent,
150
150
  SweepSetIncomplete,
151
151
  TensorSchemaMismatch,
152
+ TlsUntrusted,
152
153
  TotalBytesCap,
153
154
  TotalRefsCap,
154
155
  TrailingBytes,
@@ -306,6 +307,7 @@ __all__ = [
306
307
  "StoreRootAbsent",
307
308
  "SweepSetIncomplete",
308
309
  "TensorSchemaMismatch",
310
+ "TlsUntrusted",
309
311
  "TotalBytesCap",
310
312
  "TotalRefsCap",
311
313
  "TrailingBytes",