Metadata-Version: 2.4
Name: mini-atlas-graph-app
Version: 0.1.0
Summary: Local Mini Atlas Graph app. One Waitress process serves the Flask API and the vendored compiled UI.
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/mini-atlas/mini-atlas-graph-api-flask-python
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Flask<4,>=3
Requires-Dist: waitress<4,>=3
Requires-Dist: pydantic<3,>=2
Requires-Dist: platformdirs
Requires-Dist: tomli>=2; python_version < "3.11"
Requires-Dist: packaging
Requires-Dist: mini-atlas-graph-etl[warehouse]<0.4,>=0.3
Requires-Dist: mini-atlas-graph-warehouse<0.3,>=0.2
Requires-Dist: mini-atlas-graph-ingestor<0.4,>=0.3
Provides-Extra: postgres
Requires-Dist: psycopg[binary]<4,>=3; extra == "postgres"
Provides-Extra: odbc
Requires-Dist: pyodbc<6,>=5; extra == "odbc"
Provides-Extra: oracle
Requires-Dist: oracledb<4,>=2; extra == "oracle"
Provides-Extra: dev
Requires-Dist: pytest<9,>=8; extra == "dev"
Dynamic: license-file

# Mini Atlas Graph app

Single-user, loopback-bound, pip-installed Graph app. The console script is
`mini-atlas-graph`. Node, Docker, and Vite are **not** required at runtime.

## Install and launch

Use a dedicated virtualenv. Install the wheel (and the three library wheels it
depends on) from the files you were given until a GitHub Release has been
published to PyPI. `.github/workflows/pypi-publish.yml` is that publisher;
ordinary pushes and pull requests do not upload.

```text
python3 -m venv .venv
source .venv/bin/activate
pip install mini_atlas_graph_etl-*.whl mini_atlas_graph_warehouse-*.whl mini_atlas_graph_ingestor-*.whl
pip install mini_atlas_graph_app-0.1.0-py3-none-any.whl
```

Launch from a working directory that is **not** a source checkout, with an
empty `PYTHONPATH`, so imports resolve from site-packages:

```text
unset PYTHONPATH
mini-atlas-graph serve --data-dir /path/to/data
```

`serve` binds to loopback (`127.0.0.1:8765` by default) and prints a launch
URL whose token lives in the URL fragment. The bundled UI exchanges that
token once at `POST /api/v1/session` for an `HttpOnly; SameSite=Strict`
session cookie. API routes under `/api/v1` require that cookie. By default
`serve` opens the system browser with that launch URL. Use `--no-browser`
when you will paste the URL yourself.

Other commands:

```text
mini-atlas-graph url --data-dir /path/to/data
mini-atlas-graph doctor --data-dir /path/to/data
mini-atlas-graph backup --data-dir /path/to/data --out backup.zip
mini-atlas-graph restore backup.zip --data-dir /path/to/empty
```

`--data-dir` holds app metadata (`config.toml`, `metadata.sqlite`, recipes,
managed mapping/Avro files, jobs, uploads). Warehouse DuckDB files live under
configured `warehouse_roots`. CSV/SQLite/DuckDB **sources** live under
`input_roots`. Export folders live under `export_roots`. Create those folders
before the first launch, or let `serve` create a default layout and then edit
`config.toml`.

## Optional extras and ODBC

Core install covers local CSV, SQLite, and local DuckDB SQL. Optional extras
in `pyproject.toml` are leftover names, not the R1 source set:

| Extra | What it installs | What it does **not** prove |
| --- | --- | --- |
| `[odbc]` | `pyodbc` | An OS driver, a DSN, or a live SQL Server / BigQuery session |
| `[postgres]` | `psycopg` | PostgreSQL is **not** an R1 source |
| `[oracle]` | `oracledb` | Oracle is **not** an R1 source |

SQL Server and BigQuery connection **shapes** exist. Live corporate
authentication is unverified. Importable `pyodbc` is not an OS driver.
Expired GCP auth for BigQuery is handled outside this app; there is no
in-app Google login.

## Backup exclusions

`backup` writes `metadata.sqlite` (SQLite backup API), `recipes/`,
`managed/` mapping and Avro files, and `config.toml`. Pass
`--include-artifacts` to add `jobs/`, `runs/`, `exports/`, `uploads/`,
`imports/`, and `logs/`. **Never** copied: warehouse DuckDB files, operator
Python modules, host CSVs under `input_roots`, `instance.lock`,
`runtime.json`, and launch tokens. Restore only into a new or empty
directory; then launch that directory. Host source files used by recipes
must still be reachable under the restored app's `input_roots`.

## Trusted operator bindings

CSV wizard **names** can select callables from a trusted operator-bindings
module listed in Settings. The browser cannot upload Python. Changing or
deleting that module fails the next run explicitly. Treat the module path as
operator-controlled code, not an app sandbox.

Resume is a Run History action on an interrupted run. GraphETL validates
saved progress when Resume starts and may replay work since the last
completed round. A checkpoint filename is not a restored graph.

## HTTP API contract (F-A4)

`schema/api-v1.json` is generated from
`mini_atlas_graph_app/contracts/models.py`. Those models cover the F-06 / J1
graph-web HTTP **subset** (graphs, jobs, slice preview, exports, and related
types). They do **not** describe the expanded first-release surface:
SQL console, external ETL, config-files, backup/restore, Help search,
analytics import, Expansion/Resume, mapping/Avro, and related routes.

`npm run check:api-types` in the webapp checks the generated TypeScript
client against that pinned subset. Additional contracts live in handwritten
webapp modules (`src/api/*.ts`) and API tests. Do not treat the OpenAPI file
as the entire current API.

## Path picker, uploads, jobs

Authenticated picker routes `GET /api/v1/fs/roots` and `GET /api/v1/fs/list`
stay inside operator-configured roots. A list inspects at most 2000 directory
entries and returns at most 500 matches. When `scan_complete` is false the
page is a partial scan, not the first 500 names in the folder. Bounded
uploads go to `data/uploads/<upload_id>/` via `POST /api/v1/uploads` and are
removed by id with `DELETE /api/v1/uploads/<upload_id>`. Absolute host paths
never appear in URLs.

Jobs are submitted only through the internal `submit_job` / `JobService`
interface. Workers run as `python -m mini_atlas_graph_app.worker <job_id>`.
Authenticated reads are `GET /api/v1/jobs`, `GET /api/v1/jobs/<id>`, and
`GET /api/v1/jobs/<id>/events`.

## Known limitations

- Windows checkpoint legs were **not run** for this cut (not passed).
- Live SQL Server and BigQuery remain **unverified**.
- HTTP publication admission still content-hashes Parquet on the request
  thread (fail-closed). Light jobs share one slot. Job directories are
  retained.
- Multi-user / shared-server mode is withdrawn. Keep launch-token session
  and Host/Origin checks.
- Warehouse-upgrade UI (P2-08) is deferred. Rebuild current warehouses
  rather than in-place upgrade.

## Packaging a wheel (maintainers)

Release wheels vendor the compiled UI and a curated library-help snapshot.
Build those assets first:

```text
python scripts/vendor_frontend.py --dist <webapp>/dist --commit <pinned-sha> --version <ui-version>
python scripts/vendor_help.py
python -m build
```

Wheel and sdist builds refuse missing, extra, or tampered vendored frontend
and help files. Editable installs still work without `web/` and without Node.
`scripts/install_smoke.py` is the local installed-wheel proof.
`.github/workflows/installed-wheel.yml` is Ubuntu/macOS/Windows clean-venv
smoke; it is not a release or PyPI publish workflow.
`.github/workflows/pypi-publish.yml` vendors the same fail-closed UI, Help,
tutorial assets, and migrations, then publishes the wheel and sdist to PyPI
only when a GitHub Release is published. The release tag must be `v` plus the
exact `pyproject.toml` version. Register a PyPI Trusted Publisher for GitHub
environment `pypi` before the first upload; do not add a PyPI API-token secret.

Help and UI sources are commit-pinned in `graph_app_build.py`.
`scripts/vendor_help.py` and `scripts/assert_source_pins.py` refuse a
checkout whose HEAD is not that commit. See `docs/F07_SIBLING_CHECKOUT.md`
and `docs/F07_MIGRATION_PRESENCE.md`.
