leashbyte 0.1.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 (83) hide show
  1. leashbyte-0.1.1/.dockerignore +19 -0
  2. leashbyte-0.1.1/.env.example +46 -0
  3. leashbyte-0.1.1/.github/workflows/ci.yaml +110 -0
  4. leashbyte-0.1.1/.gitignore +46 -0
  5. leashbyte-0.1.1/CHANGELOG.md +32 -0
  6. leashbyte-0.1.1/CODE_OF_CONDUCT.md +132 -0
  7. leashbyte-0.1.1/CONTRIBUTING.md +42 -0
  8. leashbyte-0.1.1/Dockerfile +43 -0
  9. leashbyte-0.1.1/LICENSE +203 -0
  10. leashbyte-0.1.1/NOTICE +29 -0
  11. leashbyte-0.1.1/PKG-INFO +456 -0
  12. leashbyte-0.1.1/README.md +428 -0
  13. leashbyte-0.1.1/SECURITY.md +23 -0
  14. leashbyte-0.1.1/ci/compose-smoke.sh +87 -0
  15. leashbyte-0.1.1/ci/fill_fixture.py +49 -0
  16. leashbyte-0.1.1/ci/http_fixture.py +40 -0
  17. leashbyte-0.1.1/ci/mcp_roundtrip.py +38 -0
  18. leashbyte-0.1.1/compose.yaml +365 -0
  19. leashbyte-0.1.1/docs/DESIGN.md +336 -0
  20. leashbyte-0.1.1/docs/ROADMAP.md +34 -0
  21. leashbyte-0.1.1/docs/THIRD_PARTY.md +68 -0
  22. leashbyte-0.1.1/docs/python-licenses.json +327 -0
  23. leashbyte-0.1.1/examples/quickstart.py +50 -0
  24. leashbyte-0.1.1/openapi.json +2049 -0
  25. leashbyte-0.1.1/pyproject.toml +64 -0
  26. leashbyte-0.1.1/scripts/generate_openapi.py +12 -0
  27. leashbyte-0.1.1/scripts/python_licenses.py +146 -0
  28. leashbyte-0.1.1/sdk/python/LICENSE +203 -0
  29. leashbyte-0.1.1/sdk/python/NOTICE +6 -0
  30. leashbyte-0.1.1/sdk/python/README.md +35 -0
  31. leashbyte-0.1.1/sdk/python/pyproject.toml +26 -0
  32. leashbyte-0.1.1/sdk/python/src/leashbyte_sdk/__init__.py +54 -0
  33. leashbyte-0.1.1/sdk/python/src/leashbyte_sdk/_models.py +264 -0
  34. leashbyte-0.1.1/sdk/python/src/leashbyte_sdk/client.py +345 -0
  35. leashbyte-0.1.1/sdk/typescript/LICENSE +203 -0
  36. leashbyte-0.1.1/sdk/typescript/NOTICE +5 -0
  37. leashbyte-0.1.1/sdk/typescript/README.md +47 -0
  38. leashbyte-0.1.1/sdk/typescript/THIRD_PARTY_LICENSES.json +461 -0
  39. leashbyte-0.1.1/sdk/typescript/package-lock.json +1042 -0
  40. leashbyte-0.1.1/sdk/typescript/package.json +46 -0
  41. leashbyte-0.1.1/sdk/typescript/scripts/licenses.mjs +42 -0
  42. leashbyte-0.1.1/sdk/typescript/src/index.ts +321 -0
  43. leashbyte-0.1.1/sdk/typescript/src/schema.ts +1380 -0
  44. leashbyte-0.1.1/sdk/typescript/test/redaction.test.ts +76 -0
  45. leashbyte-0.1.1/sdk/typescript/tsconfig.json +18 -0
  46. leashbyte-0.1.1/site/README.md +21 -0
  47. leashbyte-0.1.1/site/index.html +18 -0
  48. leashbyte-0.1.1/site/package-lock.json +2574 -0
  49. leashbyte-0.1.1/site/package.json +24 -0
  50. leashbyte-0.1.1/site/public/favicon.svg +4 -0
  51. leashbyte-0.1.1/site/src/App.tsx +384 -0
  52. leashbyte-0.1.1/site/src/main.tsx +10 -0
  53. leashbyte-0.1.1/site/src/styles.css +1128 -0
  54. leashbyte-0.1.1/site/tsconfig.json +22 -0
  55. leashbyte-0.1.1/site/vite.config.ts +11 -0
  56. leashbyte-0.1.1/src/leashbyte/__init__.py +3 -0
  57. leashbyte-0.1.1/src/leashbyte/agent_worker.py +488 -0
  58. leashbyte-0.1.1/src/leashbyte/browser.py +627 -0
  59. leashbyte-0.1.1/src/leashbyte/browser_service.py +241 -0
  60. leashbyte-0.1.1/src/leashbyte/config.py +186 -0
  61. leashbyte-0.1.1/src/leashbyte/errors.py +11 -0
  62. leashbyte-0.1.1/src/leashbyte/fetch.py +333 -0
  63. leashbyte-0.1.1/src/leashbyte/internal.py +58 -0
  64. leashbyte-0.1.1/src/leashbyte/main.py +470 -0
  65. leashbyte-0.1.1/src/leashbyte/mcp/__init__.py +1 -0
  66. leashbyte-0.1.1/src/leashbyte/mcp/server.py +290 -0
  67. leashbyte-0.1.1/src/leashbyte/model_adapter.py +206 -0
  68. leashbyte-0.1.1/src/leashbyte/model_relay.py +84 -0
  69. leashbyte-0.1.1/src/leashbyte/models.py +142 -0
  70. leashbyte-0.1.1/src/leashbyte/proxy.py +179 -0
  71. leashbyte-0.1.1/src/leashbyte/run_models.py +164 -0
  72. leashbyte-0.1.1/src/leashbyte/run_store.py +493 -0
  73. leashbyte-0.1.1/src/leashbyte/run_viewer.py +42 -0
  74. leashbyte-0.1.1/src/leashbyte/search.py +103 -0
  75. leashbyte-0.1.1/src/leashbyte/url_guard.py +137 -0
  76. leashbyte-0.1.1/src/leashbyte/vault.py +210 -0
  77. leashbyte-0.1.1/src/leashbyte/vault_service.py +136 -0
  78. leashbyte-0.1.1/src/leashbyte/worker_service.py +19 -0
  79. leashbyte-0.1.1/tests/test_agent_security.py +343 -0
  80. leashbyte-0.1.1/tests/test_browser_security.py +480 -0
  81. leashbyte-0.1.1/tests/test_mcp_sdk_security.py +107 -0
  82. leashbyte-0.1.1/tests/test_security.py +280 -0
  83. leashbyte-0.1.1/uv.lock +1181 -0
@@ -0,0 +1,19 @@
1
+ .git
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+ .venv
6
+ venv
7
+ __pycache__
8
+ *.pyc
9
+ .pytest_cache
10
+ .ruff_cache
11
+ .github
12
+ ci
13
+ scripts
14
+ tests
15
+ docs/*
16
+ !docs/python-licenses.json
17
+ !docs/THIRD_PARTY.md
18
+ sdk/typescript/node_modules
19
+ sdk/typescript/dist
@@ -0,0 +1,46 @@
1
+ # Copy this file to .env and give each of the eight secrets below its own random value.
2
+ # openssl rand -hex 32 generates a suitable one.
3
+ LEASHBYTE_API_KEY=replace-with-a-random-api-key
4
+ LEASHBYTE_SEARXNG_SECRET_KEY=replace-with-a-different-random-secret
5
+ LEASHBYTE_SERVICE_KEY=replace-with-a-random-service-key
6
+ LEASHBYTE_VAULT_MANAGE_KEY=replace-with-a-random-vault-manage-key
7
+ LEASHBYTE_VAULT_RESOLVE_KEY=replace-with-a-random-vault-resolve-key
8
+ LEASHBYTE_CDP_KEY=replace-with-a-random-cdp-capability-key
9
+ LEASHBYTE_VAULT_MASTER_KEY=replace-with-a-random-vault-master-key
10
+ LEASHBYTE_POSTGRES_PASSWORD=replace-with-a-random-postgres-password
11
+
12
+ LEASHBYTE_BIND_ADDRESS=127.0.0.1
13
+ LEASHBYTE_PORT=8080
14
+ LEASHBYTE_MCP_BIND_ADDRESS=127.0.0.1
15
+ LEASHBYTE_MCP_PORT=8081
16
+ LEASHBYTE_SEARXNG_BASE_URL=http://searxng:8080
17
+ LEASHBYTE_BROWSER_POOL_BASE_URL=http://browser-pool:8082
18
+ LEASHBYTE_VAULT_BASE_URL=http://vault:8081
19
+ LEASHBYTE_FETCH_TIMEOUT_SECONDS=20
20
+ LEASHBYTE_FETCH_MAX_BYTES=2000000
21
+ LEASHBYTE_FETCH_MAX_REDIRECTS=5
22
+ LEASHBYTE_SEARCH_MAX_RESULTS=10
23
+ LEASHBYTE_PRIVATE_NETWORK_ALLOWLIST=
24
+ LEASHBYTE_BROWSER_MAX_SESSIONS=2
25
+ LEASHBYTE_BROWSER_LEASE_SECONDS=900
26
+ LEASHBYTE_CDP_GRANT_SECONDS=60
27
+ LEASHBYTE_BROWSER_PLUGINS_ENABLED=false
28
+ LEASHBYTE_BROWSER_PLUGIN_MODULES=
29
+ LEASHBYTE_LOCAL_MODEL_BASE_URL=
30
+ LEASHBYTE_COMPOSE_SUBNET=172.29.250.0/24
31
+ LEASHBYTE_COMPOSE_GATEWAY=172.29.250.1
32
+ LEASHBYTE_LOCAL_MODEL_PORT=11434
33
+ # Needed for local agent runs: the model name your server exposes.
34
+ LEASHBYTE_LOCAL_MODEL_NAME=
35
+ LEASHBYTE_LOCAL_MODEL_API_KEY=
36
+ LEASHBYTE_LOCAL_MODEL_API_KEY_FILE=
37
+ LEASHBYTE_MODEL_TIMEOUT_SECONDS=120
38
+ LEASHBYTE_AGENT_POLL_SECONDS=0.5
39
+ # Optional: path to an Anthropic API key file (mode 600) outside this repository.
40
+ # Leave it empty if you only use local models.
41
+ LEASHBYTE_ANTHROPIC_API_KEY_HOST_FILE=
42
+ LEASHBYTE_WEB_BOT_AUTH_KEY_FILE=
43
+ LEASHBYTE_WEB_BOT_AUTH_HOST_KEY_FILE=
44
+ LEASHBYTE_WEB_BOT_AUTH_KEY_ID=
45
+ # Optional, for a controlled private HTTPS test fixture only: its SHA-256 SPKI value.
46
+ LEASHBYTE_BROWSER_TLS_SPKI_ALLOWLIST=
@@ -0,0 +1,110 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ lint-and-test:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.13"
19
+ - name: Install uv
20
+ run: python -m pip install uv==0.13.4
21
+ - name: Install locked dependencies
22
+ run: uv sync --locked
23
+ - name: Lint
24
+ run: |
25
+ uv run ruff check src tests sdk/python/src examples ci/mcp_roundtrip.py scripts/python_licenses.py
26
+ uv run ruff format --check src tests sdk/python/src examples ci/mcp_roundtrip.py scripts/python_licenses.py
27
+ - name: Check the Python licence inventory
28
+ run: uv run python scripts/python_licenses.py --check
29
+ - name: Test essential security and API contracts
30
+ run: uv run pytest -q tests
31
+ - name: Set up Node
32
+ uses: actions/setup-node@v4
33
+ with:
34
+ node-version: "22"
35
+ cache: npm
36
+ cache-dependency-path: sdk/typescript/package-lock.json
37
+ - name: Check the TypeScript SDK and JavaScript licences
38
+ run: |
39
+ npm ci --prefix sdk/typescript
40
+ uv run python scripts/generate_openapi.py
41
+ uvx --from datamodel-code-generator==0.91.0 datamodel-codegen \
42
+ --input openapi.json \
43
+ --input-file-type openapi \
44
+ --output sdk/python/src/leashbyte_sdk/_models.py \
45
+ --output-model-type pydantic_v2.BaseModel \
46
+ --use-standard-collections \
47
+ --use-annotated \
48
+ --enum-field-as-literal all \
49
+ --target-python-version 3.13 \
50
+ --use-schema-description \
51
+ --use-field-description \
52
+ --disable-timestamp
53
+ uv run ruff format sdk/python/src/leashbyte_sdk/_models.py
54
+ npm --prefix sdk/typescript run generate
55
+ git diff --exit-code -- openapi.json sdk/python/src/leashbyte_sdk/_models.py sdk/typescript/src/schema.ts
56
+ npm --prefix sdk/typescript test
57
+ npm --prefix sdk/typescript run licenses
58
+ git diff --exit-code -- sdk/typescript/THIRD_PARTY_LICENSES.json
59
+
60
+ site:
61
+ runs-on: ubuntu-latest
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+ - name: Set up Node
65
+ uses: actions/setup-node@v4
66
+ with:
67
+ node-version: "22"
68
+ cache: npm
69
+ cache-dependency-path: site/package-lock.json
70
+ - name: Install locked site dependencies
71
+ run: npm ci --prefix site
72
+ - name: Build the static site
73
+ run: npm --prefix site run build
74
+
75
+ compose-smoke:
76
+ runs-on: ubuntu-latest
77
+ steps:
78
+ - uses: actions/checkout@v4
79
+ - uses: actions/setup-python@v5
80
+ with:
81
+ python-version: "3.13"
82
+ - name: Install uv
83
+ run: python -m pip install uv==0.13.4
84
+ - name: Install locked dependencies
85
+ run: uv sync --locked
86
+ - name: Create local configuration
87
+ run: |
88
+ cp .env.example .env
89
+ sed -i "s/replace-with-a-random-api-key/$(openssl rand -hex 32)/" .env
90
+ sed -i "s/replace-with-a-different-random-secret/$(openssl rand -hex 32)/" .env
91
+ sed -i "s/replace-with-a-random-service-key/$(openssl rand -hex 32)/" .env
92
+ sed -i "s/replace-with-a-random-vault-manage-key/$(openssl rand -hex 32)/" .env
93
+ sed -i "s/replace-with-a-random-vault-resolve-key/$(openssl rand -hex 32)/" .env
94
+ sed -i "s/replace-with-a-random-cdp-capability-key/$(openssl rand -hex 32)/" .env
95
+ sed -i "s/replace-with-a-random-vault-master-key/$(openssl rand -hex 32)/" .env
96
+ sed -i "s/replace-with-a-random-postgres-password/$(openssl rand -hex 32)/" .env
97
+ printf '%s\n' 'LEASHBYTE_PRIVATE_NETWORK_ALLOWLIST=host.docker.internal:18765' >> .env
98
+ - name: Start the controlled HTTP fixture
99
+ run: python ci/http_fixture.py &
100
+ - name: Start and check the Compose stack
101
+ run: |
102
+ docker compose up --build -d --wait
103
+ bash ci/compose-smoke.sh
104
+ LEASHBYTE_API_KEY="$(sed -n 's/^LEASHBYTE_API_KEY=//p' .env)" uv run python ci/mcp_roundtrip.py
105
+ - name: Show service logs after a failure
106
+ if: failure()
107
+ run: docker compose logs --no-color
108
+ - name: Stop the Compose stack
109
+ if: always()
110
+ run: docker compose down -v
@@ -0,0 +1,46 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .venv/
9
+ venv/
10
+ dist/
11
+ build/
12
+
13
+ # JavaScript
14
+ node_modules/
15
+ .npm/
16
+ *.tsbuildinfo
17
+
18
+ # Local configuration and secrets
19
+ .env
20
+ .env.*
21
+ !.env.example
22
+ *.pem
23
+ *.key
24
+ *.p12
25
+ *.pfx
26
+ secrets/
27
+ credentials/
28
+
29
+ # Runtime data
30
+ artifacts/
31
+ profiles/
32
+ runs/
33
+ *.sqlite
34
+ *.sqlite3
35
+
36
+ # Editors and operating systems
37
+ .idea/
38
+ .vscode/
39
+ .DS_Store
40
+ Thumbs.db
41
+
42
+ # Logs and coverage
43
+ *.log
44
+ coverage/
45
+ .coverage
46
+ htmlcov/
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ ## 0.1.1 - 2026-11-10
4
+
5
+ The first release on PyPI, as `leashbyte` and `leashbyte-sdk`. 0.1.0 was a GitHub-only release and was never uploaded to a package registry. There are no code changes.
6
+
7
+ ### Changed
8
+
9
+ - The READMEs and the project site now give the PyPI install lines.
10
+ - `leashbyte` now requires `leashbyte-sdk>=0.1.1,<0.2`. Before, it accepted any version.
11
+
12
+ ## 0.1.0 - 2026-11-10
13
+
14
+ First public source release.
15
+
16
+ ### Added
17
+
18
+ - Web search through a separate SearXNG service.
19
+ - Page fetch with Markdown, cleaned HTML and parsed JSON output, with time, size and redirect limits.
20
+ - Destination checks on fetch and browser traffic, so neither can reach internal addresses by default.
21
+ - Isolated Chrome sessions with named profiles, page actions and short-lived CDP access.
22
+ - An encrypted credential vault that only fills a credential on the exact origins it's granted to.
23
+ - Goal-driven agent runs with limits, cancellation, run records and artifacts.
24
+ - A local run viewer.
25
+ - An MCP server with stable search, fetch, browser and run tools.
26
+ - Python and TypeScript SDKs.
27
+ - A Docker Compose stack for local use, and a static project site.
28
+ - API key authentication, a security policy and Apache-2.0 licensing.
29
+
30
+ ### Notes
31
+
32
+ LeashByte is pre-alpha. Install it from GitHub and run it with Docker Compose. The PyPI and npm packages aren't published yet and the project site isn't deployed. [Known limits and next steps](docs/ROADMAP.md) lists what's still open.
@@ -0,0 +1,132 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the overall
26
+ community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or advances of
31
+ any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email address,
35
+ without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official email address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ info@leashbyte.com.
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series of
86
+ actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or permanent
93
+ ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of public interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within the
113
+ community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.1, available at
119
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120
+
121
+ Community Impact Guidelines were inspired by
122
+ [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123
+
124
+ For answers to common questions about this code of conduct, see the FAQ at
125
+ [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126
+ [https://www.contributor-covenant.org/translations][translations].
127
+
128
+ [homepage]: https://www.contributor-covenant.org
129
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130
+ [Mozilla CoC]: https://github.com/mozilla/diversity
131
+ [FAQ]: https://www.contributor-covenant.org/faq
132
+ [translations]: https://www.contributor-covenant.org/translations
@@ -0,0 +1,42 @@
1
+ # Contributing to LeashByte
2
+
3
+ LeashByte is pre-alpha and the API and security boundaries are still settling, so please open an issue before you start on anything substantial. It saves you writing a change that clashes with something already planned.
4
+
5
+ ## Running the checks
6
+
7
+ You'll need Docker with the Compose plugin, Python 3.13 with uv 0.13.4, and Node 20 or later (22.12 or later if you build the site). From the repository root:
8
+
9
+ ```bash
10
+ uv sync --locked
11
+ uv run ruff check src tests sdk/python/src examples ci/mcp_roundtrip.py scripts/python_licenses.py
12
+ uv run ruff format --check src tests sdk/python/src examples ci/mcp_roundtrip.py scripts/python_licenses.py
13
+ uv run python scripts/python_licenses.py --check
14
+ uv run pytest -q tests
15
+ npm ci --prefix sdk/typescript
16
+ npm --prefix sdk/typescript test
17
+ npm --prefix sdk/typescript run licenses
18
+ ```
19
+
20
+ CI runs the same checks on every push to `main` and every pull request, builds the static site and starts the Compose stack for a smoke test. If you change a route's contract, regenerate `openapi.json` and the SDK models as the [README](README.md#development) describes.
21
+
22
+ ## What a good pull request looks like
23
+
24
+ - It does one thing. Unrelated formatting changes and refactors make a change harder to review, so send those separately.
25
+ - The description says what changed, how you checked it and anything it doesn't cover yet. Link the issue it relates to.
26
+ - It comes with tests where they matter most: security boundaries, parsers of untrusted input and the fix for a bug, so the bug stays fixed.
27
+ - It fits the architecture in [docs/DESIGN.md](docs/DESIGN.md). If you think the design should change, say so in the issue first.
28
+ - It contains no credentials, private data, internal host names or internal paths, not even in test fixtures.
29
+
30
+ A maintainer reviews every pull request. Security-sensitive changes, and especially anything touching server-side fetch, browser access or the vault, get an extra security review.
31
+
32
+ ## New dependencies
33
+
34
+ LeashByte is Apache-2.0, and we want anyone to be able to use, modify and redistribute it under those terms without picking up extra obligations. So new code and dependencies need Apache-2.0-compatible licences, such as Apache-2.0 or MIT. AGPL, SSPL and MPL projects stay outside the repository. The licence checks above fail on anything unknown, and when you add a dependency, please keep any copyright and NOTICE files it requires.
35
+
36
+ ## Reporting a vulnerability
37
+
38
+ Please don't use a public issue or pull request for this. Email `security@leashbyte.com`. [SECURITY.md](SECURITY.md) has the details.
39
+
40
+ ## Licence and conduct
41
+
42
+ By submitting a contribution, you agree that the project may distribute it under Apache-2.0. The [Contributor Covenant](CODE_OF_CONDUCT.md) applies in all project spaces and when you represent the project in public.
@@ -0,0 +1,43 @@
1
+ FROM ghcr.io/astral-sh/uv:0.13.4 AS uv
2
+
3
+ FROM python:3.13-slim AS builder
4
+ COPY --from=uv /uv /uvx /bin/
5
+ WORKDIR /app
6
+ ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
7
+ COPY pyproject.toml uv.lock README.md LICENSE NOTICE ./
8
+ COPY sdk/python ./sdk/python
9
+ RUN uv sync --locked --no-dev --no-install-project --no-editable
10
+ COPY src ./src
11
+ RUN uv sync --locked --no-dev --no-editable
12
+
13
+ FROM python:3.13-slim AS runtime
14
+ RUN groupadd --gid 10001 leashbyte && useradd --uid 10001 --gid leashbyte --no-create-home leashbyte \
15
+ && mkdir -p /data/profiles /data/vault /data/run-artifacts \
16
+ && chown -R leashbyte:leashbyte /data
17
+ WORKDIR /app
18
+ ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1
19
+ COPY --from=builder --chown=leashbyte:leashbyte /app/.venv /app/.venv
20
+ COPY --from=builder --chown=leashbyte:leashbyte /app/src /app/src
21
+ COPY LICENSE NOTICE /app/
22
+ COPY docs/python-licenses.json docs/THIRD_PARTY.md /app/docs/
23
+ COPY sdk/typescript/THIRD_PARTY_LICENSES.json /app/sdk/typescript/
24
+ USER leashbyte
25
+ EXPOSE 8080
26
+ CMD ["uvicorn", "leashbyte.main:create_app", "--factory", "--host", "0.0.0.0", "--port", "8080"]
27
+
28
+ FROM python:3.13-slim AS browser
29
+ WORKDIR /app
30
+ ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1 PLAYWRIGHT_BROWSERS_PATH=/opt/playwright
31
+ COPY --from=builder /app/.venv /app/.venv
32
+ RUN playwright install --with-deps chrome \
33
+ && groupadd --gid 10001 leashbyte \
34
+ && useradd --uid 10001 --gid leashbyte --create-home leashbyte \
35
+ && mkdir -p /data/profiles \
36
+ && chown -R leashbyte:leashbyte /data /opt/playwright
37
+ COPY --from=builder --chown=leashbyte:leashbyte /app/src /app/src
38
+ COPY LICENSE NOTICE /app/
39
+ COPY docs/python-licenses.json docs/THIRD_PARTY.md /app/docs/
40
+ COPY sdk/typescript/THIRD_PARTY_LICENSES.json /app/sdk/typescript/
41
+ USER leashbyte
42
+ EXPOSE 8082
43
+ CMD ["uvicorn", "leashbyte.browser_service:create_browser_app", "--factory", "--host", "0.0.0.0", "--port", "8082"]