logtap 0.3.0__tar.gz → 0.4.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 (222) hide show
  1. logtap-0.4.1/.github/workflows/codeql.yml +52 -0
  2. {logtap-0.3.0 → logtap-0.4.1}/.github/workflows/tests.yml +11 -0
  3. logtap-0.4.1/PKG-INFO +304 -0
  4. logtap-0.4.1/README.md +261 -0
  5. logtap-0.4.1/SECURITY.md +165 -0
  6. logtap-0.4.1/docs/PRODUCTION_LEARNINGS.md +229 -0
  7. logtap-0.4.1/docs/plans/2026-02-02-gpu-cloud-pivot-design.md +334 -0
  8. {logtap-0.3.0 → logtap-0.4.1}/index.html +99 -48
  9. {logtap-0.3.0 → logtap-0.4.1}/pyproject.toml +1 -1
  10. logtap-0.4.1/scripts/validate_prod.py +324 -0
  11. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/__init__.py +1 -1
  12. logtap-0.4.1/src/logtap/api/app.py +111 -0
  13. logtap-0.4.1/src/logtap/api/routes/health.py +41 -0
  14. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/api/routes/logs.py +26 -31
  15. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/api/routes/parsed.py +8 -7
  16. logtap-0.4.1/src/logtap/api/routes/runs.py +330 -0
  17. logtap-0.4.1/src/logtap/cli/commands/collect.py +107 -0
  18. logtap-0.4.1/src/logtap/cli/commands/doctor.py +127 -0
  19. logtap-0.4.1/src/logtap/cli/commands/ingest.py +123 -0
  20. logtap-0.4.1/src/logtap/cli/commands/runs.py +116 -0
  21. logtap-0.4.1/src/logtap/cli/commands/tail.py +318 -0
  22. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/cli/main.py +12 -5
  23. logtap-0.4.1/src/logtap/core/runs.py +433 -0
  24. logtap-0.4.1/src/logtap/core/validation.py +184 -0
  25. logtap-0.4.1/src/logtap/models/responses.py +118 -0
  26. {logtap-0.3.0 → logtap-0.4.1}/tests/chaos/test_security.py +6 -19
  27. logtap-0.4.1/tests/unit/test_path_security.py +318 -0
  28. logtap-0.4.1/tests/unit/test_runs.py +175 -0
  29. {logtap-0.3.0 → logtap-0.4.1}/uv.lock +1 -1
  30. logtap-0.3.0/PKG-INFO +0 -319
  31. logtap-0.3.0/README.md +0 -276
  32. logtap-0.3.0/SECURITY.md +0 -20
  33. logtap-0.3.0/src/logtap/api/app.py +0 -45
  34. logtap-0.3.0/src/logtap/api/routes/health.py +0 -19
  35. logtap-0.3.0/src/logtap/cli/commands/tail.py +0 -121
  36. logtap-0.3.0/src/logtap/core/validation.py +0 -52
  37. logtap-0.3.0/src/logtap/models/responses.py +0 -65
  38. {logtap-0.3.0 → logtap-0.4.1}/.dockerignore +0 -0
  39. {logtap-0.3.0 → logtap-0.4.1}/.env.example +0 -0
  40. {logtap-0.3.0 → logtap-0.4.1}/.github/FUNDING.yml +0 -0
  41. {logtap-0.3.0 → logtap-0.4.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  42. {logtap-0.3.0 → logtap-0.4.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  43. {logtap-0.3.0 → logtap-0.4.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  44. {logtap-0.3.0 → logtap-0.4.1}/.github/workflows/publish.yml +0 -0
  45. {logtap-0.3.0 → logtap-0.4.1}/.gitignore +0 -0
  46. {logtap-0.3.0 → logtap-0.4.1}/.pre-commit-config.yaml +0 -0
  47. {logtap-0.3.0 → logtap-0.4.1}/.python-version +0 -0
  48. {logtap-0.3.0 → logtap-0.4.1}/CODE_OF_CONDUCT.md +0 -0
  49. {logtap-0.3.0 → logtap-0.4.1}/CONTRIBUTING.md +0 -0
  50. {logtap-0.3.0 → logtap-0.4.1}/Dockerfile +0 -0
  51. {logtap-0.3.0 → logtap-0.4.1}/LICENSE +0 -0
  52. {logtap-0.3.0 → logtap-0.4.1}/docker-compose.yml +0 -0
  53. {logtap-0.3.0 → logtap-0.4.1}/docs/CHAOS_TEST_REPORT.md +0 -0
  54. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/__main__.py +0 -0
  55. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/api/__init__.py +0 -0
  56. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/api/dependencies.py +0 -0
  57. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/api/routes/__init__.py +0 -0
  58. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/api/routes/files.py +0 -0
  59. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/cli/__init__.py +0 -0
  60. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/cli/commands/__init__.py +0 -0
  61. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/cli/commands/files.py +0 -0
  62. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/cli/commands/query.py +0 -0
  63. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/cli/commands/serve.py +0 -0
  64. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/__init__.py +0 -0
  65. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/parsers/__init__.py +0 -0
  66. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/parsers/apache.py +0 -0
  67. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/parsers/auto.py +0 -0
  68. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/parsers/base.py +0 -0
  69. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/parsers/json_parser.py +0 -0
  70. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/parsers/nginx.py +0 -0
  71. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/parsers/syslog.py +0 -0
  72. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/reader.py +0 -0
  73. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/core/search.py +0 -0
  74. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/models/__init__.py +0 -0
  75. {logtap-0.3.0 → logtap-0.4.1}/src/logtap/models/config.py +0 -0
  76. {logtap-0.3.0 → logtap-0.4.1}/tests/__init__.py +0 -0
  77. {logtap-0.3.0 → logtap-0.4.1}/tests/chaos/__init__.py +0 -0
  78. {logtap-0.3.0 → logtap-0.4.1}/tests/chaos/test_parsers.py +0 -0
  79. {logtap-0.3.0 → logtap-0.4.1}/tests/chaos/test_robustness.py +0 -0
  80. {logtap-0.3.0 → logtap-0.4.1}/tests/conftest.py +0 -0
  81. {logtap-0.3.0 → logtap-0.4.1}/tests/fixtures/__init__.py +0 -0
  82. {logtap-0.3.0 → logtap-0.4.1}/tests/fixtures/log/.gitkeep +0 -0
  83. {logtap-0.3.0 → logtap-0.4.1}/tests/integration/__init__.py +0 -0
  84. {logtap-0.3.0 → logtap-0.4.1}/tests/integration/test_api.py +0 -0
  85. {logtap-0.3.0 → logtap-0.4.1}/tests/log/syslog +0 -0
  86. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp05x_q6nb +0 -0
  87. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp065rykpi +0 -0
  88. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp0bm3cs8k +0 -0
  89. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp0fivu3up +0 -0
  90. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp0ijz83f3 +0 -0
  91. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp0rney_h_ +0 -0
  92. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp14lnvavq +0 -0
  93. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp1sps52p0 +0 -0
  94. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp23kjlxys +0 -0
  95. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp2dqr9age +0 -0
  96. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp34evzgj3 +0 -0
  97. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp36pg8nhr +0 -0
  98. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp3qua_9f7 +0 -0
  99. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp409e3kxw +0 -0
  100. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp40wj2d8h +0 -0
  101. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp4av7aq1x +0 -0
  102. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp4f2yhnse +0 -0
  103. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp5_fts0ah +0 -0
  104. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp5cz1fdvm +0 -0
  105. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp5fy_6kqm +0 -0
  106. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp5jkv8ly_ +0 -0
  107. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp5pyk6rzf +0 -0
  108. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp5pzzgnl_ +0 -0
  109. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp5w4es6gq +0 -0
  110. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp69zb3sz9 +0 -0
  111. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp6ap9i9r0 +0 -0
  112. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp6dhhwml0 +0 -0
  113. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp6syz7tnt +0 -0
  114. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp784bp8l5 +0 -0
  115. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp8hg6l0m8 +0 -0
  116. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp90rfsnyx +0 -0
  117. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp916cjvmi +0 -0
  118. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp91sd1e55 +0 -0
  119. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp94c0aoeb +0 -0
  120. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp98m7nh88 +0 -0
  121. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp9c6k8nk6 +0 -0
  122. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp9nwk10y6 +0 -0
  123. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp9vph0i97 +0 -0
  124. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp_81gfiki +0 -0
  125. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp_ct_338e +0 -0
  126. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp_f0hoyd4 +0 -0
  127. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmp_y251lk8 +0 -0
  128. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpaacnlvjn +0 -0
  129. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpawrsvp35 +0 -0
  130. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpb1ntqz4a +0 -0
  131. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpb7djh3dt +0 -0
  132. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpbsuxncdv +0 -0
  133. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpc6mlcsdl +0 -0
  134. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpc8qb9l4k +0 -0
  135. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpcc8h0x4q +0 -0
  136. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpcdvslz5p +0 -0
  137. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpcq19058a +0 -0
  138. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpcw2qflak +0 -0
  139. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpe3pta4d3 +0 -0
  140. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpe8493tiy +0 -0
  141. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpe946rhvt +0 -0
  142. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpef1f9h9m +0 -0
  143. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpeg2f7oov +0 -0
  144. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpepqoaana +0 -0
  145. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpf9uyvwel +0 -0
  146. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpfab61tmu +0 -0
  147. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpfu4d3omv +0 -0
  148. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpg93pkzoc +0 -0
  149. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmph17de7no +0 -0
  150. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmph59moiyt +0 -0
  151. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmphn0i0ngy +0 -0
  152. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpi25qtdum +0 -0
  153. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpiso7tb71 +0 -0
  154. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpju_l_8ur +0 -0
  155. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpkewrczka +0 -0
  156. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpkuob2ku5 +0 -0
  157. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpl1xj0zyo +0 -0
  158. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpl9rq9k_j +0 -0
  159. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpm2bmkbvd +0 -0
  160. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpm_xd1lzm +0 -0
  161. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpmc_npe2u +0 -0
  162. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpmq1l8ses +0 -0
  163. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpmvz0bev1 +0 -0
  164. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpmywg8jr4 +0 -0
  165. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpn2ep2xoe +0 -0
  166. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpn_3mnuzy +0 -0
  167. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpnmn9ob75 +0 -0
  168. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpocsk9b52 +0 -0
  169. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpod_7ghhq +0 -0
  170. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpofk_ue9w +0 -0
  171. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpp3t0hk_v +0 -0
  172. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmppas_s166 +0 -0
  173. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmppn4p6_2h +0 -0
  174. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmppv7dcstw +0 -0
  175. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmppwn77fw3 +0 -0
  176. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpq6ru59zb +0 -0
  177. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpq_k47l0a +0 -0
  178. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpqevemzw1 +0 -0
  179. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpqzd9zjft +0 -0
  180. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpr2bv_nlo +0 -0
  181. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpr4txnw42 +0 -0
  182. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmprknp7bj3 +0 -0
  183. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmprl6k40a_ +0 -0
  184. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmprl8i4tj4 +0 -0
  185. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmproqzibwd +0 -0
  186. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmprs1hp8wh +0 -0
  187. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpstuzwlgi +0 -0
  188. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpt6c4i65h +0 -0
  189. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmptd48yi53 +0 -0
  190. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmptdqndikp +0 -0
  191. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpu101z7gi +0 -0
  192. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpu2te57pf +0 -0
  193. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpu5atvpbl +0 -0
  194. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpu7ty_3jg +0 -0
  195. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpuk4xm9wu +0 -0
  196. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpupxqxpc_ +0 -0
  197. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpv318rejg +0 -0
  198. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpvcrh7utz +0 -0
  199. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpvsayrh3y +0 -0
  200. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpw_4wsps5 +0 -0
  201. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpwbcafh_z +0 -0
  202. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpwfacpdft +0 -0
  203. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpwg97gouq +0 -0
  204. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpxwt4w91l +0 -0
  205. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpy21c1m2c +0 -0
  206. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpy4qf8eco +0 -0
  207. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpy_qj2ih8 +0 -0
  208. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpya6d_rq0 +0 -0
  209. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpyf_tdrun +0 -0
  210. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpyiar72k_ +0 -0
  211. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpykep4ebm +0 -0
  212. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpyqgzw6nh +0 -0
  213. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpz4t5g3ee +0 -0
  214. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpz65snq8z +0 -0
  215. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpzaxdh_3c +0 -0
  216. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpzeplzl1z +0 -0
  217. {logtap-0.3.0 → logtap-0.4.1}/tests/log/tmpzkitocmv +0 -0
  218. {logtap-0.3.0 → logtap-0.4.1}/tests/unit/__init__.py +0 -0
  219. {logtap-0.3.0 → logtap-0.4.1}/tests/unit/test_parsers.py +0 -0
  220. {logtap-0.3.0 → logtap-0.4.1}/tests/unit/test_reader.py +0 -0
  221. {logtap-0.3.0 → logtap-0.4.1}/tests/unit/test_search.py +0 -0
  222. {logtap-0.3.0 → logtap-0.4.1}/tests/unit/test_validation.py +0 -0
@@ -0,0 +1,52 @@
1
+ name: "CodeQL"
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ paths-ignore:
7
+ - "*.md"
8
+ - "docs/**"
9
+ - ".github/FUNDING.yml"
10
+ pull_request:
11
+ branches: [ "main" ]
12
+ paths-ignore:
13
+ - "*.md"
14
+ - "docs/**"
15
+ schedule:
16
+ - cron: "0 6 * * 1" # Mondays 06:00 UTC
17
+
18
+ concurrency:
19
+ group: codeql-${{ github.ref }}
20
+ cancel-in-progress: true
21
+
22
+ jobs:
23
+ analyze:
24
+ name: Analyze (Python)
25
+ runs-on: ubuntu-latest
26
+ timeout-minutes: 60
27
+
28
+ permissions:
29
+ actions: read
30
+ contents: read
31
+ security-events: write
32
+
33
+ strategy:
34
+ fail-fast: false
35
+ matrix:
36
+ language: [ "python" ]
37
+
38
+ steps:
39
+ - name: Checkout repository
40
+ uses: actions/checkout@v4
41
+
42
+ - name: Initialize CodeQL
43
+ uses: github/codeql-action/init@v3
44
+ with:
45
+ languages: ${{ matrix.language }}
46
+ queries: security-extended
47
+
48
+ - name: Autobuild
49
+ uses: github/codeql-action/autobuild@v3
50
+
51
+ - name: Perform CodeQL Analysis
52
+ uses: github/codeql-action/analyze@v3
@@ -4,9 +4,20 @@ on:
4
4
  push:
5
5
  branches:
6
6
  - main
7
+ paths-ignore:
8
+ - "*.md"
9
+ - "docs/**"
10
+ - ".github/FUNDING.yml"
7
11
  pull_request:
8
12
  branches:
9
13
  - main
14
+ paths-ignore:
15
+ - "*.md"
16
+ - "docs/**"
17
+
18
+ concurrency:
19
+ group: tests-${{ github.ref }}
20
+ cancel-in-progress: true
10
21
 
11
22
  permissions:
12
23
  contents: read
logtap-0.4.1/PKG-INFO ADDED
@@ -0,0 +1,304 @@
1
+ Metadata-Version: 2.4
2
+ Name: logtap
3
+ Version: 0.4.1
4
+ Summary: A CLI-first log access tool for Unix systems. Remote log file access without SSH.
5
+ Project-URL: Homepage, https://github.com/cainky/logtap
6
+ Project-URL: Repository, https://github.com/cainky/logtap
7
+ Author-email: cainky <kylecain.me@gmail.com>
8
+ License: GPL-3.0-or-later
9
+ License-File: LICENSE
10
+ Keywords: cli,devops,logs,monitoring,sysadmin
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: System :: Logging
22
+ Classifier: Topic :: System :: Monitoring
23
+ Classifier: Topic :: System :: Systems Administration
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: aiofiles>=23.2.1
26
+ Requires-Dist: fastapi>=0.109.0
27
+ Requires-Dist: google-re2>=1.1
28
+ Requires-Dist: httpx>=0.26.0
29
+ Requires-Dist: pydantic-settings>=2.1.0
30
+ Requires-Dist: pydantic>=2.5.0
31
+ Requires-Dist: python-dotenv>=1.0.1
32
+ Requires-Dist: rich>=13.7.0
33
+ Requires-Dist: typer>=0.9.0
34
+ Requires-Dist: uvicorn[standard]>=0.27.0
35
+ Requires-Dist: websockets>=12.0
36
+ Provides-Extra: dev
37
+ Requires-Dist: pre-commit>=4.5.1; extra == 'dev'
38
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
39
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
40
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
41
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # logtap
45
+
46
+ [![PyPI version](https://badge.fury.io/py/logtap.svg)](https://badge.fury.io/py/logtap)
47
+ [![Tests](https://github.com/cainky/logtap/actions/workflows/tests.yml/badge.svg)](https://github.com/cainky/logtap/actions/workflows/tests.yml)
48
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
49
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
50
+
51
+ **`tail -f` for GPU clouds. Survives disconnects, aggregates multi-node.**
52
+
53
+ > Stop losing your training logs when SSH drops. Watch from anywhere, reconnect seamlessly.
54
+
55
+ ## The Problem
56
+
57
+ You're training a model on RunPod, Vast.ai, or Lambda. You SSH in, start training, and:
58
+
59
+ - Your terminal disconnects after an hour
60
+ - You lose visibility into what's happening
61
+ - You resort to tmux hacks just to keep logs alive
62
+ - Multi-node training means logs scattered across machines
63
+
64
+ ## The Solution
65
+
66
+ ```bash
67
+ # On your GPU instance
68
+ pip install logtap
69
+ logtap collect &
70
+ python train.py 2>&1 | logtap ingest run1
71
+
72
+ # From your laptop (or phone)
73
+ logtap tail run1 --follow
74
+
75
+ # Connection drops... reconnects automatically
76
+ # "reconnected (missed 0 lines)"
77
+ ```
78
+
79
+ ## Quickstart: RunPod / Vast.ai
80
+
81
+ On the GPU instance:
82
+
83
+ ```bash
84
+ pip install logtap
85
+ export LOGTAP_API_KEY=secret
86
+ logtap collect --port 8000
87
+ ```
88
+
89
+ Start training and stream logs:
90
+
91
+ ```bash
92
+ python train.py 2>&1 | logtap ingest run1 --tag node=$(hostname)
93
+ ```
94
+
95
+ From your laptop:
96
+
97
+ ```bash
98
+ export LOGTAP_SERVER=http://<gpu-ip>:8000
99
+ export LOGTAP_API_KEY=secret
100
+ logtap tail run1 --follow
101
+ ```
102
+
103
+ Disconnect, close your terminal, or switch networks.
104
+ Re-run `logtap tail` anytime to resume where you left off.
105
+
106
+ Works the same on RunPod, Vast.ai, Lambda, and any ephemeral GPU cloud.
107
+
108
+ ## Features
109
+
110
+ - **Survives Disconnects** - Resume from where you left off with cursor-based streaming
111
+ - **Pipe-Friendly** - Works with any training script via stdin
112
+ - **Multi-Node Ready** - Tag runs with `node=gpu1` and filter/aggregate
113
+ - **Zero Infra** - No database, no complex setup, just pip install
114
+ - **Lightweight** - <50MB memory, append-only file storage
115
+
116
+ ## Why not tmux / mosh?
117
+
118
+ tmux and mosh help keep SSH sessions alive.
119
+ logtap solves a different problem.
120
+
121
+ - SSH can still drop (web terminals, proxies, idle timeouts)
122
+ - tmux doesn't aggregate logs across machines
123
+ - tmux can't be viewed from another device without SSH
124
+ - tmux sessions die when ephemeral instances stop
125
+
126
+ logtap streams logs over HTTP:
127
+ - survives disconnects
128
+ - resumes without gaps
129
+ - aggregates multi-node training via tags
130
+ - works from anywhere (no SSH required)
131
+
132
+ You can still use tmux. You just don't have to rely on it.
133
+
134
+ ## Quick Start
135
+
136
+ ### 1. Install
137
+
138
+ ```bash
139
+ pip install logtap
140
+ ```
141
+
142
+ ### 2. Start Collector (on GPU instance)
143
+
144
+ ```bash
145
+ logtap collect --api-key secret
146
+ ```
147
+
148
+ ### 3. Pipe Your Training Logs
149
+
150
+ ```bash
151
+ python train.py 2>&1 | logtap ingest run1 --api-key secret
152
+ ```
153
+
154
+ ### 4. Tail From Anywhere
155
+
156
+ ```bash
157
+ export LOGTAP_SERVER=http://your-gpu-ip:8000
158
+ export LOGTAP_API_KEY=secret
159
+
160
+ logtap tail run1 --follow
161
+ ```
162
+
163
+ ## CLI Commands
164
+
165
+ | Command | Description |
166
+ |---------|-------------|
167
+ | `logtap collect` | Start collector server (accepts ingested runs) |
168
+ | `logtap ingest <run>` | Pipe stdin to collector |
169
+ | `logtap tail <run>` | Tail a run with `--follow` for streaming |
170
+ | `logtap runs` | List active runs |
171
+ | `logtap doctor` | Check server connectivity and diagnose issues |
172
+
173
+ ### Ingest Options
174
+
175
+ ```bash
176
+ # Auto-generate run name
177
+ python train.py | logtap ingest
178
+
179
+ # Add tags for multi-node
180
+ python train.py | logtap ingest run1 --tag node=gpu1 --tag rank=0
181
+
182
+ # Quiet mode (no status messages)
183
+ python train.py | logtap ingest run1 --quiet
184
+ ```
185
+
186
+ ### Tail Options
187
+
188
+ ```bash
189
+ # Follow mode (like tail -f)
190
+ logtap tail run1 --follow
191
+
192
+ # Resume from specific cursor (survives disconnects!)
193
+ logtap tail run1 --follow --since 5000
194
+
195
+ # Filter by tag
196
+ logtap tail run1 --tag node=gpu1
197
+
198
+ # Output formats
199
+ logtap tail run1 --output jsonl | jq '.line'
200
+ ```
201
+
202
+ ### Collector Options
203
+
204
+ ```bash
205
+ logtap collect \
206
+ --port 8000 \
207
+ --api-key secret \
208
+ --data-dir ~/.logtap/runs \
209
+ --max-disk-mb 5000 \
210
+ --retention-hours 72
211
+ ```
212
+
213
+ ## Multi-Node Training
214
+
215
+ Tag each node and aggregate:
216
+
217
+ ```bash
218
+ # Node 1
219
+ python train.py | logtap ingest run1 --tag node=gpu1
220
+
221
+ # Node 2
222
+ python train.py | logtap ingest run1 --tag node=gpu2
223
+
224
+ # Watch all nodes
225
+ logtap tail run1 --follow
226
+
227
+ # Watch specific node
228
+ logtap tail run1 --follow --tag node=gpu1
229
+ ```
230
+
231
+ ## Environment Variables
232
+
233
+ | Variable | Default | Description |
234
+ |----------|---------|-------------|
235
+ | `LOGTAP_SERVER` | `http://localhost:8000` | Collector URL |
236
+ | `LOGTAP_API_KEY` | - | API key for auth |
237
+
238
+ Set these to avoid typing `--server` and `--api-key` every time.
239
+
240
+ ## How It Works
241
+
242
+ 1. **Collector** writes logs to append-only files with cursor tracking
243
+ 2. **Ingest** streams stdin over HTTP chunked POST
244
+ 3. **Tail** uses SSE (Server-Sent Events) with resume support
245
+ 4. **Reconnect** passes `?since=<cursor>` to continue without gaps
246
+
247
+ No database. No message queue. Just files and HTTP.
248
+
249
+ ## API Endpoints
250
+
251
+ For scripting or custom integrations:
252
+
253
+ | Endpoint | Description |
254
+ |----------|-------------|
255
+ | `POST /runs/{id}/ingest` | Stream lines (chunked POST) |
256
+ | `GET /runs/{id}/stream` | SSE stream with `?since=&follow=` |
257
+ | `GET /runs/{id}/query` | Query with `?from=&to=&search=` |
258
+ | `GET /runs` | List runs |
259
+ | `GET /health` | Health check with capabilities |
260
+
261
+ ## Legacy: Static File Mode
262
+
263
+ logtap also works as a simple remote log viewer (the original use case):
264
+
265
+ ```bash
266
+ # On server with log files
267
+ logtap serve --log-dir /var/log
268
+
269
+ # From client
270
+ logtap tail syslog --server http://myserver:8000 --follow
271
+ logtap query auth.log --regex "Failed password"
272
+ ```
273
+
274
+ ## Security
275
+
276
+ - **API Key Auth** - Optional but recommended for production
277
+ - **Path Traversal Protection** - Comprehensive defense with symlink-safe containment checks (see [SECURITY.md](SECURITY.md))
278
+ - **ReDoS Protection** - Uses google-re2 for guaranteed linear-time regex matching
279
+ - **Read-Only by Default** - Collector only writes to its data directory
280
+ - **Input Validation** - Rejects control characters, NUL bytes, and malicious path patterns
281
+
282
+ ## Development
283
+
284
+ ```bash
285
+ git clone https://github.com/cainky/logtap.git
286
+ cd logtap
287
+
288
+ # Install with uv
289
+ uv sync --extra dev
290
+
291
+ # Run tests
292
+ uv run pytest
293
+
294
+ # Run collector in dev mode
295
+ uv run logtap collect --reload
296
+ ```
297
+
298
+ ## License
299
+
300
+ GPL v3 - see [LICENSE](LICENSE)
301
+
302
+ ## Author
303
+
304
+ Kyle Cain - [@cainky](https://github.com/cainky)
logtap-0.4.1/README.md ADDED
@@ -0,0 +1,261 @@
1
+ # logtap
2
+
3
+ [![PyPI version](https://badge.fury.io/py/logtap.svg)](https://badge.fury.io/py/logtap)
4
+ [![Tests](https://github.com/cainky/logtap/actions/workflows/tests.yml/badge.svg)](https://github.com/cainky/logtap/actions/workflows/tests.yml)
5
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
6
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
7
+
8
+ **`tail -f` for GPU clouds. Survives disconnects, aggregates multi-node.**
9
+
10
+ > Stop losing your training logs when SSH drops. Watch from anywhere, reconnect seamlessly.
11
+
12
+ ## The Problem
13
+
14
+ You're training a model on RunPod, Vast.ai, or Lambda. You SSH in, start training, and:
15
+
16
+ - Your terminal disconnects after an hour
17
+ - You lose visibility into what's happening
18
+ - You resort to tmux hacks just to keep logs alive
19
+ - Multi-node training means logs scattered across machines
20
+
21
+ ## The Solution
22
+
23
+ ```bash
24
+ # On your GPU instance
25
+ pip install logtap
26
+ logtap collect &
27
+ python train.py 2>&1 | logtap ingest run1
28
+
29
+ # From your laptop (or phone)
30
+ logtap tail run1 --follow
31
+
32
+ # Connection drops... reconnects automatically
33
+ # "reconnected (missed 0 lines)"
34
+ ```
35
+
36
+ ## Quickstart: RunPod / Vast.ai
37
+
38
+ On the GPU instance:
39
+
40
+ ```bash
41
+ pip install logtap
42
+ export LOGTAP_API_KEY=secret
43
+ logtap collect --port 8000
44
+ ```
45
+
46
+ Start training and stream logs:
47
+
48
+ ```bash
49
+ python train.py 2>&1 | logtap ingest run1 --tag node=$(hostname)
50
+ ```
51
+
52
+ From your laptop:
53
+
54
+ ```bash
55
+ export LOGTAP_SERVER=http://<gpu-ip>:8000
56
+ export LOGTAP_API_KEY=secret
57
+ logtap tail run1 --follow
58
+ ```
59
+
60
+ Disconnect, close your terminal, or switch networks.
61
+ Re-run `logtap tail` anytime to resume where you left off.
62
+
63
+ Works the same on RunPod, Vast.ai, Lambda, and any ephemeral GPU cloud.
64
+
65
+ ## Features
66
+
67
+ - **Survives Disconnects** - Resume from where you left off with cursor-based streaming
68
+ - **Pipe-Friendly** - Works with any training script via stdin
69
+ - **Multi-Node Ready** - Tag runs with `node=gpu1` and filter/aggregate
70
+ - **Zero Infra** - No database, no complex setup, just pip install
71
+ - **Lightweight** - <50MB memory, append-only file storage
72
+
73
+ ## Why not tmux / mosh?
74
+
75
+ tmux and mosh help keep SSH sessions alive.
76
+ logtap solves a different problem.
77
+
78
+ - SSH can still drop (web terminals, proxies, idle timeouts)
79
+ - tmux doesn't aggregate logs across machines
80
+ - tmux can't be viewed from another device without SSH
81
+ - tmux sessions die when ephemeral instances stop
82
+
83
+ logtap streams logs over HTTP:
84
+ - survives disconnects
85
+ - resumes without gaps
86
+ - aggregates multi-node training via tags
87
+ - works from anywhere (no SSH required)
88
+
89
+ You can still use tmux. You just don't have to rely on it.
90
+
91
+ ## Quick Start
92
+
93
+ ### 1. Install
94
+
95
+ ```bash
96
+ pip install logtap
97
+ ```
98
+
99
+ ### 2. Start Collector (on GPU instance)
100
+
101
+ ```bash
102
+ logtap collect --api-key secret
103
+ ```
104
+
105
+ ### 3. Pipe Your Training Logs
106
+
107
+ ```bash
108
+ python train.py 2>&1 | logtap ingest run1 --api-key secret
109
+ ```
110
+
111
+ ### 4. Tail From Anywhere
112
+
113
+ ```bash
114
+ export LOGTAP_SERVER=http://your-gpu-ip:8000
115
+ export LOGTAP_API_KEY=secret
116
+
117
+ logtap tail run1 --follow
118
+ ```
119
+
120
+ ## CLI Commands
121
+
122
+ | Command | Description |
123
+ |---------|-------------|
124
+ | `logtap collect` | Start collector server (accepts ingested runs) |
125
+ | `logtap ingest <run>` | Pipe stdin to collector |
126
+ | `logtap tail <run>` | Tail a run with `--follow` for streaming |
127
+ | `logtap runs` | List active runs |
128
+ | `logtap doctor` | Check server connectivity and diagnose issues |
129
+
130
+ ### Ingest Options
131
+
132
+ ```bash
133
+ # Auto-generate run name
134
+ python train.py | logtap ingest
135
+
136
+ # Add tags for multi-node
137
+ python train.py | logtap ingest run1 --tag node=gpu1 --tag rank=0
138
+
139
+ # Quiet mode (no status messages)
140
+ python train.py | logtap ingest run1 --quiet
141
+ ```
142
+
143
+ ### Tail Options
144
+
145
+ ```bash
146
+ # Follow mode (like tail -f)
147
+ logtap tail run1 --follow
148
+
149
+ # Resume from specific cursor (survives disconnects!)
150
+ logtap tail run1 --follow --since 5000
151
+
152
+ # Filter by tag
153
+ logtap tail run1 --tag node=gpu1
154
+
155
+ # Output formats
156
+ logtap tail run1 --output jsonl | jq '.line'
157
+ ```
158
+
159
+ ### Collector Options
160
+
161
+ ```bash
162
+ logtap collect \
163
+ --port 8000 \
164
+ --api-key secret \
165
+ --data-dir ~/.logtap/runs \
166
+ --max-disk-mb 5000 \
167
+ --retention-hours 72
168
+ ```
169
+
170
+ ## Multi-Node Training
171
+
172
+ Tag each node and aggregate:
173
+
174
+ ```bash
175
+ # Node 1
176
+ python train.py | logtap ingest run1 --tag node=gpu1
177
+
178
+ # Node 2
179
+ python train.py | logtap ingest run1 --tag node=gpu2
180
+
181
+ # Watch all nodes
182
+ logtap tail run1 --follow
183
+
184
+ # Watch specific node
185
+ logtap tail run1 --follow --tag node=gpu1
186
+ ```
187
+
188
+ ## Environment Variables
189
+
190
+ | Variable | Default | Description |
191
+ |----------|---------|-------------|
192
+ | `LOGTAP_SERVER` | `http://localhost:8000` | Collector URL |
193
+ | `LOGTAP_API_KEY` | - | API key for auth |
194
+
195
+ Set these to avoid typing `--server` and `--api-key` every time.
196
+
197
+ ## How It Works
198
+
199
+ 1. **Collector** writes logs to append-only files with cursor tracking
200
+ 2. **Ingest** streams stdin over HTTP chunked POST
201
+ 3. **Tail** uses SSE (Server-Sent Events) with resume support
202
+ 4. **Reconnect** passes `?since=<cursor>` to continue without gaps
203
+
204
+ No database. No message queue. Just files and HTTP.
205
+
206
+ ## API Endpoints
207
+
208
+ For scripting or custom integrations:
209
+
210
+ | Endpoint | Description |
211
+ |----------|-------------|
212
+ | `POST /runs/{id}/ingest` | Stream lines (chunked POST) |
213
+ | `GET /runs/{id}/stream` | SSE stream with `?since=&follow=` |
214
+ | `GET /runs/{id}/query` | Query with `?from=&to=&search=` |
215
+ | `GET /runs` | List runs |
216
+ | `GET /health` | Health check with capabilities |
217
+
218
+ ## Legacy: Static File Mode
219
+
220
+ logtap also works as a simple remote log viewer (the original use case):
221
+
222
+ ```bash
223
+ # On server with log files
224
+ logtap serve --log-dir /var/log
225
+
226
+ # From client
227
+ logtap tail syslog --server http://myserver:8000 --follow
228
+ logtap query auth.log --regex "Failed password"
229
+ ```
230
+
231
+ ## Security
232
+
233
+ - **API Key Auth** - Optional but recommended for production
234
+ - **Path Traversal Protection** - Comprehensive defense with symlink-safe containment checks (see [SECURITY.md](SECURITY.md))
235
+ - **ReDoS Protection** - Uses google-re2 for guaranteed linear-time regex matching
236
+ - **Read-Only by Default** - Collector only writes to its data directory
237
+ - **Input Validation** - Rejects control characters, NUL bytes, and malicious path patterns
238
+
239
+ ## Development
240
+
241
+ ```bash
242
+ git clone https://github.com/cainky/logtap.git
243
+ cd logtap
244
+
245
+ # Install with uv
246
+ uv sync --extra dev
247
+
248
+ # Run tests
249
+ uv run pytest
250
+
251
+ # Run collector in dev mode
252
+ uv run logtap collect --reload
253
+ ```
254
+
255
+ ## License
256
+
257
+ GPL v3 - see [LICENSE](LICENSE)
258
+
259
+ ## Author
260
+
261
+ Kyle Cain - [@cainky](https://github.com/cainky)