pactown 0.1.16__tar.gz → 0.1.47__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.
- {pactown-0.1.16 → pactown-0.1.47}/.bumpversion.cfg +1 -1
- pactown-0.1.47/CHANGELOG.md +92 -0
- {pactown-0.1.16 → pactown-0.1.47}/PKG-INFO +5 -1
- {pactown-0.1.16 → pactown-0.1.47}/docs/CONFIGURATION.md +5 -1
- {pactown-0.1.16 → pactown-0.1.47}/docs/DEPLOYMENT.md +5 -1
- {pactown-0.1.16 → pactown-0.1.47}/docs/NETWORK.md +5 -1
- {pactown-0.1.16 → pactown-0.1.47}/docs/SPECIFICATION.md +4 -0
- pactown-0.1.47/examples/fast-start-demo/README.md +82 -0
- pactown-0.1.47/examples/fast-start-demo/demo.py +120 -0
- pactown-0.1.47/examples/security-policy/README.md +92 -0
- pactown-0.1.47/examples/security-policy/demo.py +105 -0
- pactown-0.1.47/examples/user-isolation/README.md +94 -0
- pactown-0.1.47/examples/user-isolation/demo.py +110 -0
- {pactown-0.1.16 → pactown-0.1.47}/project.functions.toon +303 -85
- {pactown-0.1.16 → pactown-0.1.47}/project.toon +143 -17
- {pactown-0.1.16 → pactown-0.1.47}/pyproject.toml +9 -1
- pactown-0.1.47/src/pactown/__init__.py +197 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/cli.py +272 -0
- pactown-0.1.47/src/pactown/events.py +1066 -0
- pactown-0.1.47/src/pactown/llm.py +450 -0
- pactown-0.1.47/src/pactown/markpact_blocks.py +50 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/network.py +26 -4
- pactown-0.1.47/src/pactown/platform.py +146 -0
- pactown-0.1.47/src/pactown/runner_api.py +458 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/sandbox_manager.py +253 -11
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/service_runner.py +105 -10
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/user_isolation.py +125 -23
- pactown-0.1.47/tests/test_llm.py +311 -0
- pactown-0.1.47/tests/test_platform.py +56 -0
- pactown-0.1.47/tests/test_runner_api.py +108 -0
- pactown-0.1.47/tests/test_service_runner_fast_run_fallback.py +75 -0
- pactown-0.1.47/tests/test_user_isolation_manager.py +152 -0
- pactown-0.1.16/CHANGELOG.md +0 -36
- pactown-0.1.16/src/pactown/__init__.py +0 -91
- pactown-0.1.16/src/pactown/markpact_blocks.py +0 -32
- {pactown-0.1.16 → pactown-0.1.47}/.gitignore +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/LICENSE +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/Makefile +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/TODO.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/docs/CLOUDFLARE_WORKERS_COMPARISON.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/docs/FAST_START.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/docs/GENERATOR.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/docs/LOGGING.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/docs/QUADLET.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/docs/SECURITY.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/docs/SECURITY_POLICY.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/docs/USER_ISOLATION.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/api-gateway-webhooks/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/email-llm-responder/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/microservices/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/microservices/saas.pactown.yaml +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/microservices/services/go-gateway/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/microservices/services/node-api/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/microservices/services/python-ml/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/quadlet-vps/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/realtime-notifications/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/saas-platform/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/saas-platform/saas.pactown.yaml +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/saas-platform/services/api/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/saas-platform/services/cli/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/saas-platform/services/database/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/saas-platform/services/gateway/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples/saas-platform/services/web/README.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/examples.md +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/img.png +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/project.sh +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/project.toon-schema.json +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/saas.pactown.yaml +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/config.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/deploy/__init__.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/deploy/base.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/deploy/compose.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/deploy/docker.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/deploy/kubernetes.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/deploy/podman.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/deploy/quadlet.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/deploy/quadlet_api.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/deploy/quadlet_shell.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/fast_start.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/generator.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/orchestrator.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/parallel.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/registry/__init__.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/registry/client.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/registry/models.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/registry/server.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/resolver.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/src/pactown/security.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/tests/__init__.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/tests/test_config.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/tests/test_deploy_dockerfile.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/tests/test_markpact_blocks.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/tests/test_network.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/tests/test_parallel.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/tests/test_quadlet_security.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/tests/test_registry.py +0 -0
- {pactown-0.1.16 → pactown-0.1.47}/tests/test_resolver.py +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.4.0] - 2026-01-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Fast Start Module** (`fast_start.py`)
|
|
13
|
+
- Dependency caching with hash-based venv reuse
|
|
14
|
+
- ~50-100ms startup for cached deps vs ~5-10s fresh
|
|
15
|
+
- `ServiceRunner.fast_run()` method
|
|
16
|
+
- Parallel file writing for sandbox creation
|
|
17
|
+
|
|
18
|
+
- **Security Policy Module** (`security.py`)
|
|
19
|
+
- Rate limiting with token bucket algorithm
|
|
20
|
+
- User profiles with tier-based limits (FREE/BASIC/PRO/ENTERPRISE)
|
|
21
|
+
- Concurrent service limits per user
|
|
22
|
+
- Anomaly logging for admin monitoring
|
|
23
|
+
- Server load throttling
|
|
24
|
+
|
|
25
|
+
- **User Isolation Module** (`user_isolation.py`)
|
|
26
|
+
- Linux user-based sandbox isolation
|
|
27
|
+
- Per-SaaS-user home directories
|
|
28
|
+
- Process isolation with different UIDs
|
|
29
|
+
- Export/import for user data migration
|
|
30
|
+
- REST API endpoints for user management
|
|
31
|
+
|
|
32
|
+
- **Detailed Logging**
|
|
33
|
+
- Structured logging in sandbox_manager
|
|
34
|
+
- STDERR/STDOUT capture on process failure
|
|
35
|
+
- Signal interpretation (SIGTERM, SIGKILL, etc.)
|
|
36
|
+
- Per-service error log files
|
|
37
|
+
|
|
38
|
+
- **New Documentation**
|
|
39
|
+
- `docs/FAST_START.md` - Dependency caching guide
|
|
40
|
+
- `docs/SECURITY_POLICY.md` - Rate limiting and user profiles
|
|
41
|
+
- `docs/USER_ISOLATION.md` - Multi-tenant isolation
|
|
42
|
+
- `docs/LOGGING.md` - Structured logging guide
|
|
43
|
+
- Navigation links across all docs
|
|
44
|
+
|
|
45
|
+
- **New Examples**
|
|
46
|
+
- `examples/fast-start-demo/` - Fast startup with caching
|
|
47
|
+
- `examples/security-policy/` - Rate limiting demo
|
|
48
|
+
- `examples/user-isolation/` - Multi-tenant isolation demo
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- README.md restructured with feature menu and quick navigation
|
|
53
|
+
- All docs updated with cross-links for easier navigation
|
|
54
|
+
- sandbox_manager.py: Better error capture and signal handling
|
|
55
|
+
- service_runner.py: Added delays to prevent race conditions on restart
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
|
|
59
|
+
- Process killed by SIGTERM on restart (race condition)
|
|
60
|
+
- Truncated error output from crashed processes
|
|
61
|
+
- **Hardcoded port mismatch** - Auto-replace hardcoded ports (e.g., `--port 8009`) with requested port
|
|
62
|
+
- PORT and MARKPACT_PORT environment variables now always set
|
|
63
|
+
|
|
64
|
+
## [Unreleased]
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- Podman Quadlet deployment backend (`pactown.deploy.quadlet`) with templates, backend operations, and Traefik integration.
|
|
69
|
+
- Interactive Quadlet shell (`pactown quadlet shell`).
|
|
70
|
+
- Quadlet REST API (`pactown quadlet api`) and entrypoint `pactown-quadlet-api`.
|
|
71
|
+
- Security hardening and injection test suite (`tests/test_quadlet_security.py`).
|
|
72
|
+
- Quadlet security guide (`docs/SECURITY.md`).
|
|
73
|
+
- Cloudflare Workers comparison (`docs/CLOUDFLARE_WORKERS_COMPARISON.md`).
|
|
74
|
+
- Practical Quadlet examples in `examples/*` where the user edits only `README.md` (embedded code blocks) and deployment artifacts are generated into `./sandbox`.
|
|
75
|
+
|
|
76
|
+
### Changed
|
|
77
|
+
|
|
78
|
+
- Dockerfile Python healthcheck now uses `MARKPACT_PORT` with fallback to `PORT` to maintain compatibility.
|
|
79
|
+
- Registry timestamps use timezone-aware datetimes (`datetime.now(timezone.utc)`) to avoid Python 3.13 deprecations.
|
|
80
|
+
- Makefile:
|
|
81
|
+
- Prefers project venv python if present.
|
|
82
|
+
- `lint`/`format` fall back to `pipx run ruff` when ruff is not installed in the interpreter.
|
|
83
|
+
- `test` explicitly loads `pytest_asyncio.plugin` to work with `PYTEST_DISABLE_PLUGIN_AUTOLOAD=1`.
|
|
84
|
+
|
|
85
|
+
### Fixed
|
|
86
|
+
|
|
87
|
+
- Multiple Quadlet injection vectors (container name, env var, volume, Traefik label, systemd unit) mitigated via input sanitization.
|
|
88
|
+
- Ruff lint issues across `src/` and `tests/`.
|
|
89
|
+
|
|
90
|
+
## [0.1.5]
|
|
91
|
+
|
|
92
|
+
- Initial public version.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pactown
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.47
|
|
4
4
|
Summary: Decentralized Service Ecosystem Orchestrator - Build interconnected microservices from Markdown using markpact
|
|
5
5
|
Project-URL: Homepage, https://github.com/wronai/pactown
|
|
6
6
|
Project-URL: Repository, https://github.com/wronai/pactown
|
|
@@ -30,6 +30,8 @@ Requires-Dist: pyyaml>=6.0
|
|
|
30
30
|
Requires-Dist: rich>=13.0
|
|
31
31
|
Requires-Dist: uvicorn>=0.20.0
|
|
32
32
|
Requires-Dist: watchfiles>=0.20.0
|
|
33
|
+
Provides-Extra: all
|
|
34
|
+
Requires-Dist: lolm>=0.1.6; extra == 'all'
|
|
33
35
|
Provides-Extra: dev
|
|
34
36
|
Requires-Dist: build; extra == 'dev'
|
|
35
37
|
Requires-Dist: bump2version>=1.0; extra == 'dev'
|
|
@@ -38,6 +40,8 @@ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
|
38
40
|
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
39
41
|
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
40
42
|
Requires-Dist: twine; extra == 'dev'
|
|
43
|
+
Provides-Extra: llm
|
|
44
|
+
Requires-Dist: lolm>=0.1.6; extra == 'llm'
|
|
41
45
|
Description-Content-Type: text/markdown
|
|
42
46
|
|
|
43
47
|

|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Configuration Reference
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[← Back to README](../README.md) | [Specification](SPECIFICATION.md) | [Network](NETWORK.md) | [Deployment →](DEPLOYMENT.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
> **Related:** [Generator](GENERATOR.md) | [Fast Start](FAST_START.md) | [Security Policy](SECURITY_POLICY.md)
|
|
4
8
|
|
|
5
9
|
Complete reference for `saas.pactown.yaml` configuration files.
|
|
6
10
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Production Deployment Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[← Back to README](../README.md) | [Configuration](CONFIGURATION.md) | [Quadlet →](QUADLET.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
> **Related:** [Network](NETWORK.md) | [Security](SECURITY.md) | [User Isolation](USER_ISOLATION.md)
|
|
4
8
|
|
|
5
9
|
This guide covers deploying pactown ecosystems to production using Docker, Podman, and Kubernetes.
|
|
6
10
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Network & Service Discovery
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[← Back to README](../README.md) | [Configuration](CONFIGURATION.md) | [Deployment →](DEPLOYMENT.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
> **Related:** [Specification](SPECIFICATION.md) | [Generator](GENERATOR.md)
|
|
4
8
|
|
|
5
9
|
This document describes pactown's dynamic port allocation and service discovery system.
|
|
6
10
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Pactown System Specification
|
|
2
2
|
|
|
3
|
+
[← Back to README](../README.md) | [Configuration →](CONFIGURATION.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
3
7
|
## Problem Statement
|
|
4
8
|
|
|
5
9
|
Modern software systems are increasingly composed of multiple independent services that need to work together. However, setting up and managing these systems presents several challenges:
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Fast Start Demo
|
|
2
|
+
|
|
3
|
+
This example demonstrates pactown's fast startup capabilities with dependency caching.
|
|
4
|
+
|
|
5
|
+
## What This Shows
|
|
6
|
+
|
|
7
|
+
- **First run**: Dependencies are installed and cached (~5-10s)
|
|
8
|
+
- **Second run**: Cached venv is reused (~50-100ms)
|
|
9
|
+
- **Same deps = same cache**: Projects with identical dependencies share the cache
|
|
10
|
+
|
|
11
|
+
## Files
|
|
12
|
+
|
|
13
|
+
- `demo.py` - Python script demonstrating fast_run vs regular run
|
|
14
|
+
- `api/README.md` - Sample FastAPI service
|
|
15
|
+
- `api2/README.md` - Second service with same deps (will use cache)
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Run the demo
|
|
21
|
+
python demo.py
|
|
22
|
+
|
|
23
|
+
# Or use pactown CLI
|
|
24
|
+
pactown up fast-start.pactown.yaml
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Expected Output
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
=== Fast Start Demo ===
|
|
31
|
+
|
|
32
|
+
Run 1 (fresh):
|
|
33
|
+
Creating sandbox...
|
|
34
|
+
Installing dependencies (fastapi, uvicorn)...
|
|
35
|
+
✓ Started in 5.2s
|
|
36
|
+
|
|
37
|
+
Run 2 (cached):
|
|
38
|
+
⚡ Cache hit! Reusing venv
|
|
39
|
+
✓ Started in 0.08s
|
|
40
|
+
|
|
41
|
+
Speedup: 65x faster!
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## How It Works
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from pactown import ServiceRunner
|
|
48
|
+
|
|
49
|
+
runner = ServiceRunner(enable_fast_start=True)
|
|
50
|
+
|
|
51
|
+
# First run - creates and caches venv
|
|
52
|
+
result1 = await runner.fast_run(
|
|
53
|
+
service_id="api-1",
|
|
54
|
+
content=open("api/README.md").read(),
|
|
55
|
+
port=8001,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Second run - reuses cached venv
|
|
59
|
+
result2 = await runner.fast_run(
|
|
60
|
+
service_id="api-2",
|
|
61
|
+
content=open("api2/README.md").read(), # Same deps!
|
|
62
|
+
port=8002,
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Cache Location
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
/tmp/pactown-sandboxes/
|
|
70
|
+
├── .cache/
|
|
71
|
+
│ └── venvs/
|
|
72
|
+
│ └── venv_a1b2c3d4/ # Cached: fastapi + uvicorn
|
|
73
|
+
├── api-1/
|
|
74
|
+
│ └── .venv -> ../.cache/venvs/venv_a1b2c3d4
|
|
75
|
+
└── api-2/
|
|
76
|
+
└── .venv -> ../.cache/venvs/venv_a1b2c3d4 # Same cache!
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Related Documentation
|
|
80
|
+
|
|
81
|
+
- [Fast Start Guide](../../docs/FAST_START.md)
|
|
82
|
+
- [Security Policy](../../docs/SECURITY_POLICY.md)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Fast Start Demo - Demonstrates dependency caching for fast startup.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python demo.py
|
|
7
|
+
"""
|
|
8
|
+
import asyncio
|
|
9
|
+
import time
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
# Add pactown to path if running from source
|
|
13
|
+
import sys
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
|
|
15
|
+
|
|
16
|
+
from pactown import ServiceRunner
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
API_README = '''# Demo API
|
|
20
|
+
|
|
21
|
+
Simple FastAPI service for demonstrating fast start.
|
|
22
|
+
|
|
23
|
+
```python markpact:deps
|
|
24
|
+
fastapi
|
|
25
|
+
uvicorn
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```python markpact:file path=main.py
|
|
29
|
+
from fastapi import FastAPI
|
|
30
|
+
|
|
31
|
+
app = FastAPI(title="Fast Start Demo")
|
|
32
|
+
|
|
33
|
+
@app.get("/")
|
|
34
|
+
def root():
|
|
35
|
+
return {"message": "Hello from Fast Start Demo!"}
|
|
36
|
+
|
|
37
|
+
@app.get("/health")
|
|
38
|
+
def health():
|
|
39
|
+
return {"status": "ok"}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```bash markpact:run
|
|
43
|
+
uvicorn main:app --host 0.0.0.0 --port $PORT
|
|
44
|
+
```
|
|
45
|
+
'''
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def main():
|
|
49
|
+
print("=" * 50)
|
|
50
|
+
print("⚡ Fast Start Demo")
|
|
51
|
+
print("=" * 50)
|
|
52
|
+
print()
|
|
53
|
+
|
|
54
|
+
runner = ServiceRunner(
|
|
55
|
+
sandbox_root="/tmp/fast-start-demo",
|
|
56
|
+
enable_fast_start=True,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Run 1 - First run (cold start)
|
|
60
|
+
print("Run 1 (fresh install):")
|
|
61
|
+
print(" Creating sandbox and installing dependencies...")
|
|
62
|
+
|
|
63
|
+
start = time.time()
|
|
64
|
+
result1 = await runner.fast_run(
|
|
65
|
+
service_id="demo-api-1",
|
|
66
|
+
content=API_README,
|
|
67
|
+
port=10091,
|
|
68
|
+
)
|
|
69
|
+
time1 = time.time() - start
|
|
70
|
+
|
|
71
|
+
if result1.success:
|
|
72
|
+
print(f" ✓ Started in {time1:.2f}s")
|
|
73
|
+
else:
|
|
74
|
+
print(f" ❌ Failed: {result1.message}")
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
# Stop first service
|
|
78
|
+
runner.stop("demo-api-1")
|
|
79
|
+
|
|
80
|
+
print()
|
|
81
|
+
|
|
82
|
+
# Run 2 - Second run (cache hit)
|
|
83
|
+
print("Run 2 (cached):")
|
|
84
|
+
print(" ⚡ Checking cache...")
|
|
85
|
+
|
|
86
|
+
start = time.time()
|
|
87
|
+
result2 = await runner.fast_run(
|
|
88
|
+
service_id="demo-api-2",
|
|
89
|
+
content=API_README, # Same deps!
|
|
90
|
+
port=10092,
|
|
91
|
+
)
|
|
92
|
+
time2 = time.time() - start
|
|
93
|
+
|
|
94
|
+
if result2.success:
|
|
95
|
+
print(f" ✓ Started in {time2:.2f}s")
|
|
96
|
+
else:
|
|
97
|
+
print(f" ❌ Failed: {result2.message}")
|
|
98
|
+
|
|
99
|
+
# Stop second service
|
|
100
|
+
runner.stop("demo-api-2")
|
|
101
|
+
|
|
102
|
+
print()
|
|
103
|
+
print("=" * 50)
|
|
104
|
+
print(f"Results:")
|
|
105
|
+
print(f" First run: {time1:.2f}s")
|
|
106
|
+
print(f" Second run: {time2:.2f}s")
|
|
107
|
+
if time2 > 0:
|
|
108
|
+
print(f" Speedup: {time1/time2:.1f}x faster!")
|
|
109
|
+
print("=" * 50)
|
|
110
|
+
|
|
111
|
+
# Show cache stats
|
|
112
|
+
stats = runner.get_cache_stats()
|
|
113
|
+
print()
|
|
114
|
+
print("Cache Statistics:")
|
|
115
|
+
print(f" Entries: {stats.get('cache_entries', 0)}")
|
|
116
|
+
print(f" Caching enabled: {stats.get('caching_enabled', False)}")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Security Policy Demo
|
|
2
|
+
|
|
3
|
+
This example demonstrates pactown's security policy features for multi-tenant SaaS.
|
|
4
|
+
|
|
5
|
+
## What This Shows
|
|
6
|
+
|
|
7
|
+
- **Rate limiting** - Token bucket algorithm to prevent abuse
|
|
8
|
+
- **User profiles** - Tier-based resource limits (FREE/BASIC/PRO)
|
|
9
|
+
- **Concurrent limits** - Max services per user
|
|
10
|
+
- **Anomaly logging** - Track and alert on security events
|
|
11
|
+
|
|
12
|
+
## Files
|
|
13
|
+
|
|
14
|
+
- `demo.py` - Python script demonstrating security features
|
|
15
|
+
- `multi_tenant.py` - Multi-tenant SaaS simulation
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Run the demo
|
|
21
|
+
python demo.py
|
|
22
|
+
|
|
23
|
+
# Or run the multi-tenant simulation
|
|
24
|
+
python multi_tenant.py
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Expected Output
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
=== Security Policy Demo ===
|
|
31
|
+
|
|
32
|
+
Creating user profiles...
|
|
33
|
+
✓ free_user (FREE tier): max 2 concurrent services
|
|
34
|
+
✓ pro_user (PRO tier): max 10 concurrent services
|
|
35
|
+
|
|
36
|
+
Testing rate limiting...
|
|
37
|
+
Request 1: ✓ allowed
|
|
38
|
+
Request 2: ✓ allowed
|
|
39
|
+
...
|
|
40
|
+
Request 21: ✗ rate limited (wait 2.5s)
|
|
41
|
+
|
|
42
|
+
Testing concurrent limits...
|
|
43
|
+
free_user starting service 1: ✓ allowed
|
|
44
|
+
free_user starting service 2: ✓ allowed
|
|
45
|
+
free_user starting service 3: ✗ limit reached (2/2)
|
|
46
|
+
|
|
47
|
+
Anomaly log:
|
|
48
|
+
[RATE_LIMIT_EXCEEDED] free_user - Rate limit exceeded
|
|
49
|
+
[CONCURRENT_LIMIT_EXCEEDED] free_user - Max 2 services
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## User Tiers
|
|
53
|
+
|
|
54
|
+
| Tier | Concurrent | Memory | Requests/min |
|
|
55
|
+
|------|------------|--------|--------------|
|
|
56
|
+
| FREE | 2 | 256MB | 20 |
|
|
57
|
+
| BASIC | 5 | 512MB | 60 |
|
|
58
|
+
| PRO | 10 | 2GB | 120 |
|
|
59
|
+
| ENTERPRISE | 50 | 8GB | 500 |
|
|
60
|
+
|
|
61
|
+
## Code Example
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from pactown import SecurityPolicy, UserProfile, UserTier
|
|
65
|
+
|
|
66
|
+
# Create security policy
|
|
67
|
+
policy = SecurityPolicy(
|
|
68
|
+
anomaly_log_path=Path("./anomalies.jsonl"),
|
|
69
|
+
default_rate_limit=60,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Create user profile
|
|
73
|
+
profile = UserProfile.from_tier("user123", UserTier.PRO)
|
|
74
|
+
policy.set_user_profile(profile)
|
|
75
|
+
|
|
76
|
+
# Check if user can start service
|
|
77
|
+
result = await policy.check_can_start_service(
|
|
78
|
+
user_id="user123",
|
|
79
|
+
service_id="my-api",
|
|
80
|
+
port=8001,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if result.allowed:
|
|
84
|
+
print("✓ Starting service...")
|
|
85
|
+
else:
|
|
86
|
+
print(f"✗ Denied: {result.reason}")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Related Documentation
|
|
90
|
+
|
|
91
|
+
- [Security Policy Guide](../../docs/SECURITY_POLICY.md)
|
|
92
|
+
- [User Isolation](../../docs/USER_ISOLATION.md)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Security Policy Demo - Demonstrates rate limiting and user profiles.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python demo.py
|
|
7
|
+
"""
|
|
8
|
+
import asyncio
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
# Add pactown to path if running from source
|
|
12
|
+
import sys
|
|
13
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
|
|
14
|
+
|
|
15
|
+
from pactown import SecurityPolicy, UserProfile, UserTier, AnomalyType
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def main():
|
|
19
|
+
print("=" * 50)
|
|
20
|
+
print("🛡️ Security Policy Demo")
|
|
21
|
+
print("=" * 50)
|
|
22
|
+
print()
|
|
23
|
+
|
|
24
|
+
# Create security policy
|
|
25
|
+
policy = SecurityPolicy(
|
|
26
|
+
anomaly_log_path=Path("./demo_anomalies.jsonl"),
|
|
27
|
+
default_rate_limit=20, # 20 requests per minute for demo
|
|
28
|
+
cpu_threshold=80.0,
|
|
29
|
+
memory_threshold=85.0,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Create user profiles
|
|
33
|
+
print("Creating user profiles...")
|
|
34
|
+
|
|
35
|
+
free_user = UserProfile.from_tier("free_user", UserTier.FREE)
|
|
36
|
+
pro_user = UserProfile.from_tier("pro_user", UserTier.PRO)
|
|
37
|
+
|
|
38
|
+
policy.set_user_profile(free_user)
|
|
39
|
+
policy.set_user_profile(pro_user)
|
|
40
|
+
|
|
41
|
+
print(f" ✓ free_user (FREE): max {free_user.max_concurrent_services} services, {free_user.max_requests_per_minute} req/min")
|
|
42
|
+
print(f" ✓ pro_user (PRO): max {pro_user.max_concurrent_services} services, {pro_user.max_requests_per_minute} req/min")
|
|
43
|
+
print()
|
|
44
|
+
|
|
45
|
+
# Test rate limiting
|
|
46
|
+
print("Testing rate limiting (free_user)...")
|
|
47
|
+
|
|
48
|
+
for i in range(25):
|
|
49
|
+
result = await policy.check_can_start_service(
|
|
50
|
+
user_id="free_user",
|
|
51
|
+
service_id=f"test-service-{i}",
|
|
52
|
+
port=10000 + i,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if result.allowed:
|
|
56
|
+
print(f" Request {i+1}: ✓ allowed")
|
|
57
|
+
else:
|
|
58
|
+
print(f" Request {i+1}: ✗ {result.reason}")
|
|
59
|
+
if result.wait_seconds:
|
|
60
|
+
print(f" Wait {result.wait_seconds:.1f}s before retry")
|
|
61
|
+
break
|
|
62
|
+
|
|
63
|
+
print()
|
|
64
|
+
|
|
65
|
+
# Test concurrent limits
|
|
66
|
+
print("Testing concurrent service limits (free_user)...")
|
|
67
|
+
|
|
68
|
+
# Simulate starting services
|
|
69
|
+
for i in range(4):
|
|
70
|
+
service_id = f"concurrent-test-{i}"
|
|
71
|
+
|
|
72
|
+
# Check if allowed
|
|
73
|
+
result = await policy.check_can_start_service(
|
|
74
|
+
user_id="free_user",
|
|
75
|
+
service_id=service_id,
|
|
76
|
+
port=11000 + i,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if result.allowed:
|
|
80
|
+
# Register the service as running
|
|
81
|
+
policy.register_service("free_user", service_id)
|
|
82
|
+
count = policy.get_user_service_count("free_user")
|
|
83
|
+
print(f" Service {i+1}: ✓ started (running: {count}/{free_user.max_concurrent_services})")
|
|
84
|
+
else:
|
|
85
|
+
print(f" Service {i+1}: ✗ {result.reason}")
|
|
86
|
+
|
|
87
|
+
print()
|
|
88
|
+
|
|
89
|
+
# Show anomaly summary
|
|
90
|
+
print("Anomaly summary:")
|
|
91
|
+
summary = policy.get_anomaly_summary(hours=1)
|
|
92
|
+
print(f" Total events: {summary.get('total_count', 0)}")
|
|
93
|
+
|
|
94
|
+
by_type = summary.get('by_type', {})
|
|
95
|
+
for anomaly_type, count in by_type.items():
|
|
96
|
+
print(f" - {anomaly_type}: {count}")
|
|
97
|
+
|
|
98
|
+
print()
|
|
99
|
+
print("=" * 50)
|
|
100
|
+
print("Demo complete! Check demo_anomalies.jsonl for logged events.")
|
|
101
|
+
print("=" * 50)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# User Isolation Demo
|
|
2
|
+
|
|
3
|
+
This example demonstrates pactown's Linux user-based sandbox isolation for multi-tenant SaaS.
|
|
4
|
+
|
|
5
|
+
## What This Shows
|
|
6
|
+
|
|
7
|
+
- **Linux user creation** - Each SaaS user gets a dedicated Linux user
|
|
8
|
+
- **Process isolation** - Different UIDs prevent cross-tenant access
|
|
9
|
+
- **File isolation** - Separate home directories per user
|
|
10
|
+
- **Migration support** - Export/import user data
|
|
11
|
+
|
|
12
|
+
## Files
|
|
13
|
+
|
|
14
|
+
- `demo.py` - Python script demonstrating user isolation
|
|
15
|
+
- `migration.py` - Demonstrates user data migration
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Run the demo (works in non-root mode too)
|
|
21
|
+
python demo.py
|
|
22
|
+
|
|
23
|
+
# Run migration demo
|
|
24
|
+
python migration.py
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Expected Output
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
=== User Isolation Demo ===
|
|
31
|
+
|
|
32
|
+
Creating isolated users...
|
|
33
|
+
✓ user_alice -> pactown_a1b2c3d4 (UID: 60001)
|
|
34
|
+
✓ user_bob -> pactown_e5f6g7h8 (UID: 60002)
|
|
35
|
+
|
|
36
|
+
Creating sandboxes...
|
|
37
|
+
✓ user_alice/my-api: /home/pactown_users/pactown_a1b2c3d4/sandboxes/my-api
|
|
38
|
+
✓ user_bob/my-api: /home/pactown_users/pactown_e5f6g7h8/sandboxes/my-api
|
|
39
|
+
|
|
40
|
+
User statistics:
|
|
41
|
+
user_alice: 1 sandbox, 0.5 MB
|
|
42
|
+
user_bob: 1 sandbox, 0.5 MB
|
|
43
|
+
|
|
44
|
+
Total isolated users: 2
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Architecture
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
/home/pactown_users/
|
|
51
|
+
├── pactown_a1b2c3d4/ # user_alice (UID 60001)
|
|
52
|
+
│ ├── sandboxes/
|
|
53
|
+
│ │ └── my-api/
|
|
54
|
+
│ │ ├── main.py
|
|
55
|
+
│ │ └── .venv/
|
|
56
|
+
│ └── .cache/
|
|
57
|
+
└── pactown_e5f6g7h8/ # user_bob (UID 60002)
|
|
58
|
+
├── sandboxes/
|
|
59
|
+
│ └── my-api/
|
|
60
|
+
└── .cache/
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Code Example
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from pactown import UserIsolationManager, get_isolation_manager
|
|
67
|
+
|
|
68
|
+
# Get global manager
|
|
69
|
+
manager = get_isolation_manager()
|
|
70
|
+
|
|
71
|
+
# Create isolated user
|
|
72
|
+
user = manager.get_or_create_user("alice@example.com")
|
|
73
|
+
print(f"Linux user: {user.linux_username}")
|
|
74
|
+
print(f"UID: {user.linux_uid}")
|
|
75
|
+
print(f"Home: {user.home_dir}")
|
|
76
|
+
|
|
77
|
+
# Get sandbox path
|
|
78
|
+
sandbox = manager.get_sandbox_path("alice@example.com", "my-api")
|
|
79
|
+
|
|
80
|
+
# Run command as isolated user
|
|
81
|
+
process = manager.run_as_user(
|
|
82
|
+
saas_user_id="alice@example.com",
|
|
83
|
+
command="python main.py",
|
|
84
|
+
cwd=sandbox,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Export for migration
|
|
88
|
+
manager.export_user_data("alice@example.com", Path("/backup/alice.tar.gz"))
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Related Documentation
|
|
92
|
+
|
|
93
|
+
- [User Isolation Guide](../../docs/USER_ISOLATION.md)
|
|
94
|
+
- [Security Policy](../../docs/SECURITY_POLICY.md)
|