isolinear 0.1.0__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.
- isolinear-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +41 -0
- isolinear-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- isolinear-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- isolinear-0.1.0/.github/workflows/ci.yml +59 -0
- isolinear-0.1.0/.github/workflows/release.yml +43 -0
- isolinear-0.1.0/.gitignore +8 -0
- isolinear-0.1.0/CHANGELOG.md +27 -0
- isolinear-0.1.0/CONTRIBUTING.md +62 -0
- isolinear-0.1.0/LICENSE +21 -0
- isolinear-0.1.0/PKG-INFO +163 -0
- isolinear-0.1.0/README.md +132 -0
- isolinear-0.1.0/SECURITY.md +32 -0
- isolinear-0.1.0/docs/USER_STORIES.md +63 -0
- isolinear-0.1.0/docs/img/auth.png +0 -0
- isolinear-0.1.0/docs/img/browse.png +0 -0
- isolinear-0.1.0/docs/img/login.png +0 -0
- isolinear-0.1.0/docs/img/perms.png +0 -0
- isolinear-0.1.0/mise.toml +7 -0
- isolinear-0.1.0/pyproject.toml +87 -0
- isolinear-0.1.0/src/isolinear/__init__.py +0 -0
- isolinear-0.1.0/src/isolinear/__main__.py +4 -0
- isolinear-0.1.0/src/isolinear/app.py +71 -0
- isolinear-0.1.0/src/isolinear/application/__init__.py +16 -0
- isolinear-0.1.0/src/isolinear/application/onboarding.py +64 -0
- isolinear-0.1.0/src/isolinear/application/read_model.py +73 -0
- isolinear-0.1.0/src/isolinear/application/workspace.py +141 -0
- isolinear-0.1.0/src/isolinear/domain/__init__.py +46 -0
- isolinear-0.1.0/src/isolinear/domain/errors.py +12 -0
- isolinear-0.1.0/src/isolinear/domain/host.py +15 -0
- isolinear-0.1.0/src/isolinear/domain/models.py +102 -0
- isolinear-0.1.0/src/isolinear/domain/permissions.py +58 -0
- isolinear-0.1.0/src/isolinear/domain/ports.py +50 -0
- isolinear-0.1.0/src/isolinear/domain/secret_store.py +30 -0
- isolinear-0.1.0/src/isolinear/infrastructure/__init__.py +18 -0
- isolinear-0.1.0/src/isolinear/infrastructure/config.py +13 -0
- isolinear-0.1.0/src/isolinear/infrastructure/connector.py +113 -0
- isolinear-0.1.0/src/isolinear/infrastructure/databricks.py +154 -0
- isolinear-0.1.0/src/isolinear/infrastructure/profiles.py +66 -0
- isolinear-0.1.0/src/isolinear/interface/__init__.py +0 -0
- isolinear-0.1.0/src/isolinear/interface/modals.py +360 -0
- isolinear-0.1.0/src/isolinear/interface/screens/__init__.py +0 -0
- isolinear-0.1.0/src/isolinear/interface/screens/login.py +292 -0
- isolinear-0.1.0/src/isolinear/interface/screens/main.py +591 -0
- isolinear-0.1.0/src/isolinear/interface/theme.py +84 -0
- isolinear-0.1.0/src/isolinear/interface/widgets.py +321 -0
- isolinear-0.1.0/src/isolinear/styles.tcss +260 -0
- isolinear-0.1.0/tests/conftest.py +24 -0
- isolinear-0.1.0/tests/fakes.py +147 -0
- isolinear-0.1.0/tests/test_cache.py +35 -0
- isolinear-0.1.0/tests/test_databricks.py +107 -0
- isolinear-0.1.0/tests/test_host.py +22 -0
- isolinear-0.1.0/tests/test_models.py +48 -0
- isolinear-0.1.0/tests/test_permissions.py +23 -0
- isolinear-0.1.0/tests/test_profiles.py +76 -0
- isolinear-0.1.0/tests/test_session.py +87 -0
- isolinear-0.1.0/tests/test_ui_browse.py +126 -0
- isolinear-0.1.0/tests/test_ui_login.py +36 -0
- isolinear-0.1.0/tests/test_ui_permissions.py +65 -0
- isolinear-0.1.0/uv.lock +1385 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something isn't working
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened?
|
|
9
|
+
description: What did you do, what did you expect, and what happened instead?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: repro
|
|
14
|
+
attributes:
|
|
15
|
+
label: Steps to reproduce
|
|
16
|
+
placeholder: |
|
|
17
|
+
1. Run `isolinear`
|
|
18
|
+
2. ...
|
|
19
|
+
- type: input
|
|
20
|
+
id: version
|
|
21
|
+
attributes:
|
|
22
|
+
label: Isolinear version
|
|
23
|
+
placeholder: "0.1.0 (uvx isolinear --version, or `uv tool list`)"
|
|
24
|
+
- type: input
|
|
25
|
+
id: python
|
|
26
|
+
attributes:
|
|
27
|
+
label: Python version
|
|
28
|
+
placeholder: "3.12"
|
|
29
|
+
- type: input
|
|
30
|
+
id: os
|
|
31
|
+
attributes:
|
|
32
|
+
label: OS / terminal
|
|
33
|
+
placeholder: "macOS 15 · iTerm2"
|
|
34
|
+
- type: textarea
|
|
35
|
+
id: logs
|
|
36
|
+
attributes:
|
|
37
|
+
label: Error output
|
|
38
|
+
description: >
|
|
39
|
+
The TUI takes over the screen, so capture stderr with
|
|
40
|
+
`isolinear 2>err.log` and paste `err.log` here.
|
|
41
|
+
render: text
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!-- Thanks for contributing to Isolinear! -->
|
|
2
|
+
|
|
3
|
+
## What & why
|
|
4
|
+
|
|
5
|
+
<!-- What does this change, and why? Link any related issue (#123). -->
|
|
6
|
+
|
|
7
|
+
## Checklist
|
|
8
|
+
|
|
9
|
+
- [ ] `uv run pytest` passes
|
|
10
|
+
- [ ] `uv run ruff check .` and `uv run ruff format .` are clean
|
|
11
|
+
- [ ] `uv run ty check` passes
|
|
12
|
+
- [ ] New behaviour has a test
|
|
13
|
+
- [ ] Business logic stays out of `interface/`; the SDK stays in `infrastructure/`
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
name: lint + types
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v6
|
|
15
|
+
with:
|
|
16
|
+
enable-cache: true
|
|
17
|
+
- run: uv sync
|
|
18
|
+
- name: Lint (ruff)
|
|
19
|
+
run: uv run ruff check .
|
|
20
|
+
- name: Format check (ruff)
|
|
21
|
+
run: uv run ruff format --check .
|
|
22
|
+
- name: Type check (ty)
|
|
23
|
+
run: uv run ty check
|
|
24
|
+
|
|
25
|
+
test:
|
|
26
|
+
name: test (py${{ matrix.python-version }} · ${{ matrix.os }})
|
|
27
|
+
runs-on: ${{ matrix.os }}
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
os: [ubuntu-latest]
|
|
32
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
33
|
+
include:
|
|
34
|
+
- os: macos-latest
|
|
35
|
+
python-version: "3.14"
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
- uses: astral-sh/setup-uv@v6
|
|
39
|
+
with:
|
|
40
|
+
enable-cache: true
|
|
41
|
+
- name: Tests (pytest)
|
|
42
|
+
run: uv run --python ${{ matrix.python-version }} pytest
|
|
43
|
+
|
|
44
|
+
build:
|
|
45
|
+
name: build (wheel + sdist)
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
- uses: astral-sh/setup-uv@v6
|
|
50
|
+
- run: uv build
|
|
51
|
+
- name: Smoke-test the built wheel on 3.11
|
|
52
|
+
run: |
|
|
53
|
+
uv venv /tmp/smoke -p 3.11
|
|
54
|
+
uv pip install --python /tmp/smoke/bin/python dist/*.whl
|
|
55
|
+
/tmp/smoke/bin/python -c "import isolinear.app; print('import OK')"
|
|
56
|
+
- uses: actions/upload-artifact@v4
|
|
57
|
+
with:
|
|
58
|
+
name: dist
|
|
59
|
+
path: dist/*
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Publish to PyPI when a version tag is pushed, e.g. `git tag v0.1.0 && git push --tags`.
|
|
4
|
+
# Uses PyPI Trusted Publishing (OIDC) — no API token needed. Configure a trusted
|
|
5
|
+
# publisher for this repo + workflow at https://pypi.org/manage/account/publishing/.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
pypi:
|
|
14
|
+
name: build + publish to PyPI
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
environment: pypi
|
|
17
|
+
permissions:
|
|
18
|
+
id-token: write # required for trusted publishing
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: astral-sh/setup-uv@v6
|
|
22
|
+
|
|
23
|
+
- name: Build
|
|
24
|
+
run: uv build
|
|
25
|
+
|
|
26
|
+
- name: Publish
|
|
27
|
+
run: uv publish --trusted-publishing always
|
|
28
|
+
|
|
29
|
+
github-release:
|
|
30
|
+
name: attach artifacts to the GitHub release
|
|
31
|
+
needs: pypi
|
|
32
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
permissions:
|
|
35
|
+
contents: write
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
- uses: astral-sh/setup-uv@v6
|
|
39
|
+
- run: uv build
|
|
40
|
+
- uses: softprops/action-gh-release@v2
|
|
41
|
+
with:
|
|
42
|
+
files: dist/*
|
|
43
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0]
|
|
10
|
+
|
|
11
|
+
Initial release.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Three-pane terminal browser for Databricks scopes, secrets, and ACLs.
|
|
16
|
+
- Onboarding: OAuth browser login by workspace URL, account-level workspace
|
|
17
|
+
discovery (AWS / Azure / GCP), and saved `~/.databrickscfg` profiles.
|
|
18
|
+
- Create / edit / delete secrets, create / delete scopes, and manage scope
|
|
19
|
+
permissions (ACLs).
|
|
20
|
+
- Reveal & copy secret values (fetched lazily; never bulk-loaded).
|
|
21
|
+
- Authorization overview of your effective permission per scope.
|
|
22
|
+
- Fuzzy filter (`/`), command palette (`ctrl+p`), vim + arrow navigation.
|
|
23
|
+
- Pre-loads and caches scopes/secrets/ACLs on startup.
|
|
24
|
+
- Three switchable themes (violet, amber Okudagram, phosphor green).
|
|
25
|
+
|
|
26
|
+
[Unreleased]: https://github.com/misja-pronk/isolinear/compare/v0.1.0...HEAD
|
|
27
|
+
[0.1.0]: https://github.com/misja-pronk/isolinear/releases/tag/v0.1.0
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Contributing to Isolinear
|
|
2
|
+
|
|
3
|
+
Thanks for your interest! Issues and pull requests are very welcome.
|
|
4
|
+
|
|
5
|
+
## Toolchain
|
|
6
|
+
|
|
7
|
+
Isolinear uses [`mise`](https://mise.jdx.dev) to pin tools and the all-Astral
|
|
8
|
+
stack — [`uv`](https://docs.astral.sh/uv/) (env / deps / run),
|
|
9
|
+
[`ruff`](https://docs.astral.sh/ruff/) (lint + format), and
|
|
10
|
+
[`ty`](https://docs.astral.sh/ty/) (type check).
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
mise install # installs the pinned Python + uv (optional but recommended)
|
|
14
|
+
uv sync # creates .venv and installs deps + dev tools
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Day-to-day
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
uv run isolinear # run the app
|
|
21
|
+
uv run textual run --dev isolinear.app:IsolinearApp # with Textual devtools
|
|
22
|
+
|
|
23
|
+
uv run pytest # test suite
|
|
24
|
+
uv run ruff check . && uv run ruff format . # lint + format
|
|
25
|
+
uv run ty check # type check
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
All four of these run in CI on every push/PR — please make sure they're green
|
|
29
|
+
before opening a PR. New behaviour should come with a test.
|
|
30
|
+
|
|
31
|
+
## Architecture
|
|
32
|
+
|
|
33
|
+
The codebase is hexagonal / DDD; the dependency rule is enforced by convention:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
interface → application → domain ← infrastructure
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- **`domain/`** — pure model, rules, and ports (`Protocol`s). No Textual, SDK,
|
|
40
|
+
or asyncio.
|
|
41
|
+
- **`application/`** — use-cases (`WorkspaceService`, `OnboardingService`) and
|
|
42
|
+
the in-memory read model. Depends only on `domain`.
|
|
43
|
+
- **`infrastructure/`** — adapters that implement the ports; the *only* place
|
|
44
|
+
that imports the Databricks SDK.
|
|
45
|
+
- **`interface/`** — the Textual UI. Talks to `application` + `domain` only.
|
|
46
|
+
- **`app.py`** — the composition root that wires the adapters together.
|
|
47
|
+
|
|
48
|
+
Keep business logic out of the UI, keep the SDK out of everything but
|
|
49
|
+
`infrastructure/`, and tests can substitute the in-memory fakes in `tests/`.
|
|
50
|
+
|
|
51
|
+
## Tests
|
|
52
|
+
|
|
53
|
+
- `domain` / `application` are covered by fast unit tests with fake ports.
|
|
54
|
+
- The UI is driven through Textual's `Pilot` harness — no network, no real
|
|
55
|
+
Databricks. See `tests/fakes.py` for the in-memory doubles.
|
|
56
|
+
|
|
57
|
+
## Commits & PRs
|
|
58
|
+
|
|
59
|
+
- Small, focused PRs are easiest to review.
|
|
60
|
+
- Describe the *why*, not just the *what*.
|
|
61
|
+
- By contributing you agree your work is licensed under the project's
|
|
62
|
+
[MIT License](LICENSE).
|
isolinear-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Misja Pronk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
isolinear-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: isolinear
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A keyboard-driven terminal UI for managing Databricks secrets
|
|
5
|
+
Project-URL: Homepage, https://github.com/misja-pronk/isolinear
|
|
6
|
+
Project-URL: Repository, https://github.com/misja-pronk/isolinear
|
|
7
|
+
Project-URL: Issues, https://github.com/misja-pronk/isolinear/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/misja-pronk/isolinear/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Misja Pronk <misja@prorexconsultancy.nl>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cli,databricks,secret-manager,secrets,security,terminal,textual,tui
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: System Administrators
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Topic :: Terminals
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Requires-Dist: databricks-sdk>=0.30
|
|
29
|
+
Requires-Dist: textual>=2.0
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# Isolinear ▦
|
|
33
|
+
|
|
34
|
+
**A keyboard-driven terminal UI for managing Databricks secrets.**
|
|
35
|
+
Browse workspaces, scopes, secrets and ACLs; create / edit / delete; reveal &
|
|
36
|
+
copy values — all from a fast, LCARS-flavoured TUI.
|
|
37
|
+
|
|
38
|
+
[](https://github.com/misja-pronk/isolinear/actions/workflows/ci.yml)
|
|
39
|
+
[](https://pypi.org/project/isolinear/)
|
|
40
|
+
[](https://pypi.org/project/isolinear/)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
[](https://github.com/astral-sh/ruff)
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
Run it with [uv](https://docs.astral.sh/uv/) — no clone, no virtualenv:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
uvx isolinear # run once, ephemerally
|
|
52
|
+
uv tool install isolinear # install the `isolinear` (and `iso`) commands on PATH
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or with pipx: `pipx run isolinear` / `pipx install isolinear`.
|
|
56
|
+
|
|
57
|
+
> Requires Python ≥ 3.11. Built with [Textual](https://textual.textualize.io)
|
|
58
|
+
> and the [Databricks SDK](https://github.com/databricks/databricks-sdk-py).
|
|
59
|
+
|
|
60
|
+
## Quickstart
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
isolinear # or: iso
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
You **don't** need to pre-configure anything. On first launch Isolinear opens an
|
|
67
|
+
onboarding hub with two doors:
|
|
68
|
+
|
|
69
|
+
1. **Workspace URL** — paste a workspace URL and sign in through your browser
|
|
70
|
+
(OAuth U2M / SSO). No token required.
|
|
71
|
+
2. **Discover via account** — pick your cloud (AWS / Azure / GCP), paste your
|
|
72
|
+
Account ID, sign in once, and Isolinear lists **every workspace in the
|
|
73
|
+
account** for you to choose from.
|
|
74
|
+
|
|
75
|
+
Tick *save as profile* and it writes a reusable entry to `~/.databrickscfg`
|
|
76
|
+
(host + `auth_type = external-browser`, **no secret stored** — same as
|
|
77
|
+
`databricks auth login`), so next launch connects instantly. Existing
|
|
78
|
+
`~/.databrickscfg` profiles show up automatically.
|
|
79
|
+
|
|
80
|
+
<table>
|
|
81
|
+
<tr>
|
|
82
|
+
<td><img src="docs/img/login.png" alt="Login hub"></td>
|
|
83
|
+
<td><img src="docs/img/auth.png" alt="Authorization overview"></td>
|
|
84
|
+
</tr>
|
|
85
|
+
<tr>
|
|
86
|
+
<td align="center"><sub>Onboarding hub</sub></td>
|
|
87
|
+
<td align="center"><sub>Authorization overview</sub></td>
|
|
88
|
+
</tr>
|
|
89
|
+
</table>
|
|
90
|
+
|
|
91
|
+
## Features
|
|
92
|
+
|
|
93
|
+
- **Three-pane browser** — scopes (with secret counts + your access), secrets
|
|
94
|
+
(with relative age), and a detail pane.
|
|
95
|
+
- **Reveal & copy** secret values; values are fetched lazily on reveal and never
|
|
96
|
+
bulk-pulled into memory.
|
|
97
|
+
- **Full CRUD** — create / edit / delete secrets, create / delete scopes,
|
|
98
|
+
manage scope **permissions (ACLs)**.
|
|
99
|
+
- **Authorization overview** — your effective permission on every scope.
|
|
100
|
+
- **Fuzzy filter** (`/`), **command palette** (`ctrl+p`), vim + arrow navigation.
|
|
101
|
+
- **Multiple workspaces** — switch profiles, or add new connections on the fly.
|
|
102
|
+
- **Pre-loads & caches** everything on startup for an instant experience.
|
|
103
|
+
- Three switchable themes (violet, amber Okudagram, phosphor green).
|
|
104
|
+
|
|
105
|
+
## Keys
|
|
106
|
+
|
|
107
|
+
Everything is keyboard driven. Press `?` for the in-app cheat-sheet or `ctrl+p`
|
|
108
|
+
for the fuzzy command palette.
|
|
109
|
+
|
|
110
|
+
| Key | Action |
|
|
111
|
+
|-----|--------|
|
|
112
|
+
| `↑↓` / `j` `k` | Move within a pane |
|
|
113
|
+
| `←→` / `h` `l` · `tab` | Move between panes |
|
|
114
|
+
| `g` / `G` | Jump to top / bottom |
|
|
115
|
+
| `/` | Filter the focused pane |
|
|
116
|
+
| `n` / `N` | New secret / new scope |
|
|
117
|
+
| `e` · `d` | Edit secret · delete (with confirm) |
|
|
118
|
+
| `p` | Manage scope permissions (ACLs) |
|
|
119
|
+
| `space` · `c` | Reveal / hide value · copy value |
|
|
120
|
+
| `r` / `R` | Refresh scope / workspace |
|
|
121
|
+
| `a` | Authorization overview |
|
|
122
|
+
| `w` · `ctrl+p` | Switch workspace · command palette |
|
|
123
|
+
| `?` · `q` | Help · quit |
|
|
124
|
+
|
|
125
|
+
## Security
|
|
126
|
+
|
|
127
|
+
Isolinear talks to Databricks through the official SDK's unified auth. It does
|
|
128
|
+
**not** store secret *values* — they're read on demand and kept only in memory.
|
|
129
|
+
Saved profiles contain a host + `auth_type`, never a token. See
|
|
130
|
+
[SECURITY.md](SECURITY.md) for details and how to report a vulnerability.
|
|
131
|
+
|
|
132
|
+
## How it's built
|
|
133
|
+
|
|
134
|
+
Hexagonal / DDD layers; dependencies point inward and **all I/O is behind domain
|
|
135
|
+
ports**, so the UI never touches the SDK and the whole domain is unit-testable
|
|
136
|
+
without a network:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
isolinear/
|
|
140
|
+
domain/ model, rules + ports (SecretStore, WorkspaceConnector, ProfileStore)
|
|
141
|
+
application/ use-cases (WorkspaceService, OnboardingService) + read model
|
|
142
|
+
infrastructure/ adapters — the only Databricks-SDK importers
|
|
143
|
+
interface/ Textual presentation (no business logic, no infra)
|
|
144
|
+
app.py composition root
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Contributing
|
|
148
|
+
|
|
149
|
+
Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). The toolkit is
|
|
150
|
+
all-[Astral](https://astral.sh): **uv** (env/deps/run), **ruff** (lint+format),
|
|
151
|
+
**ty** (types).
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
uv sync
|
|
155
|
+
uv run pytest # tests (core units + UI via Textual Pilot)
|
|
156
|
+
uv run ruff check . # lint
|
|
157
|
+
uv run ty check # types
|
|
158
|
+
uv run isolinear # run it
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
[MIT](LICENSE) © Misja Pronk
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Isolinear ▦
|
|
2
|
+
|
|
3
|
+
**A keyboard-driven terminal UI for managing Databricks secrets.**
|
|
4
|
+
Browse workspaces, scopes, secrets and ACLs; create / edit / delete; reveal &
|
|
5
|
+
copy values — all from a fast, LCARS-flavoured TUI.
|
|
6
|
+
|
|
7
|
+
[](https://github.com/misja-pronk/isolinear/actions/workflows/ci.yml)
|
|
8
|
+
[](https://pypi.org/project/isolinear/)
|
|
9
|
+
[](https://pypi.org/project/isolinear/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
[](https://github.com/astral-sh/ruff)
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
Run it with [uv](https://docs.astral.sh/uv/) — no clone, no virtualenv:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
uvx isolinear # run once, ephemerally
|
|
21
|
+
uv tool install isolinear # install the `isolinear` (and `iso`) commands on PATH
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or with pipx: `pipx run isolinear` / `pipx install isolinear`.
|
|
25
|
+
|
|
26
|
+
> Requires Python ≥ 3.11. Built with [Textual](https://textual.textualize.io)
|
|
27
|
+
> and the [Databricks SDK](https://github.com/databricks/databricks-sdk-py).
|
|
28
|
+
|
|
29
|
+
## Quickstart
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
isolinear # or: iso
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
You **don't** need to pre-configure anything. On first launch Isolinear opens an
|
|
36
|
+
onboarding hub with two doors:
|
|
37
|
+
|
|
38
|
+
1. **Workspace URL** — paste a workspace URL and sign in through your browser
|
|
39
|
+
(OAuth U2M / SSO). No token required.
|
|
40
|
+
2. **Discover via account** — pick your cloud (AWS / Azure / GCP), paste your
|
|
41
|
+
Account ID, sign in once, and Isolinear lists **every workspace in the
|
|
42
|
+
account** for you to choose from.
|
|
43
|
+
|
|
44
|
+
Tick *save as profile* and it writes a reusable entry to `~/.databrickscfg`
|
|
45
|
+
(host + `auth_type = external-browser`, **no secret stored** — same as
|
|
46
|
+
`databricks auth login`), so next launch connects instantly. Existing
|
|
47
|
+
`~/.databrickscfg` profiles show up automatically.
|
|
48
|
+
|
|
49
|
+
<table>
|
|
50
|
+
<tr>
|
|
51
|
+
<td><img src="docs/img/login.png" alt="Login hub"></td>
|
|
52
|
+
<td><img src="docs/img/auth.png" alt="Authorization overview"></td>
|
|
53
|
+
</tr>
|
|
54
|
+
<tr>
|
|
55
|
+
<td align="center"><sub>Onboarding hub</sub></td>
|
|
56
|
+
<td align="center"><sub>Authorization overview</sub></td>
|
|
57
|
+
</tr>
|
|
58
|
+
</table>
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
- **Three-pane browser** — scopes (with secret counts + your access), secrets
|
|
63
|
+
(with relative age), and a detail pane.
|
|
64
|
+
- **Reveal & copy** secret values; values are fetched lazily on reveal and never
|
|
65
|
+
bulk-pulled into memory.
|
|
66
|
+
- **Full CRUD** — create / edit / delete secrets, create / delete scopes,
|
|
67
|
+
manage scope **permissions (ACLs)**.
|
|
68
|
+
- **Authorization overview** — your effective permission on every scope.
|
|
69
|
+
- **Fuzzy filter** (`/`), **command palette** (`ctrl+p`), vim + arrow navigation.
|
|
70
|
+
- **Multiple workspaces** — switch profiles, or add new connections on the fly.
|
|
71
|
+
- **Pre-loads & caches** everything on startup for an instant experience.
|
|
72
|
+
- Three switchable themes (violet, amber Okudagram, phosphor green).
|
|
73
|
+
|
|
74
|
+
## Keys
|
|
75
|
+
|
|
76
|
+
Everything is keyboard driven. Press `?` for the in-app cheat-sheet or `ctrl+p`
|
|
77
|
+
for the fuzzy command palette.
|
|
78
|
+
|
|
79
|
+
| Key | Action |
|
|
80
|
+
|-----|--------|
|
|
81
|
+
| `↑↓` / `j` `k` | Move within a pane |
|
|
82
|
+
| `←→` / `h` `l` · `tab` | Move between panes |
|
|
83
|
+
| `g` / `G` | Jump to top / bottom |
|
|
84
|
+
| `/` | Filter the focused pane |
|
|
85
|
+
| `n` / `N` | New secret / new scope |
|
|
86
|
+
| `e` · `d` | Edit secret · delete (with confirm) |
|
|
87
|
+
| `p` | Manage scope permissions (ACLs) |
|
|
88
|
+
| `space` · `c` | Reveal / hide value · copy value |
|
|
89
|
+
| `r` / `R` | Refresh scope / workspace |
|
|
90
|
+
| `a` | Authorization overview |
|
|
91
|
+
| `w` · `ctrl+p` | Switch workspace · command palette |
|
|
92
|
+
| `?` · `q` | Help · quit |
|
|
93
|
+
|
|
94
|
+
## Security
|
|
95
|
+
|
|
96
|
+
Isolinear talks to Databricks through the official SDK's unified auth. It does
|
|
97
|
+
**not** store secret *values* — they're read on demand and kept only in memory.
|
|
98
|
+
Saved profiles contain a host + `auth_type`, never a token. See
|
|
99
|
+
[SECURITY.md](SECURITY.md) for details and how to report a vulnerability.
|
|
100
|
+
|
|
101
|
+
## How it's built
|
|
102
|
+
|
|
103
|
+
Hexagonal / DDD layers; dependencies point inward and **all I/O is behind domain
|
|
104
|
+
ports**, so the UI never touches the SDK and the whole domain is unit-testable
|
|
105
|
+
without a network:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
isolinear/
|
|
109
|
+
domain/ model, rules + ports (SecretStore, WorkspaceConnector, ProfileStore)
|
|
110
|
+
application/ use-cases (WorkspaceService, OnboardingService) + read model
|
|
111
|
+
infrastructure/ adapters — the only Databricks-SDK importers
|
|
112
|
+
interface/ Textual presentation (no business logic, no infra)
|
|
113
|
+
app.py composition root
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Contributing
|
|
117
|
+
|
|
118
|
+
Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). The toolkit is
|
|
119
|
+
all-[Astral](https://astral.sh): **uv** (env/deps/run), **ruff** (lint+format),
|
|
120
|
+
**ty** (types).
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
uv sync
|
|
124
|
+
uv run pytest # tests (core units + UI via Textual Pilot)
|
|
125
|
+
uv run ruff check . # lint
|
|
126
|
+
uv run ty check # types
|
|
127
|
+
uv run isolinear # run it
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
[MIT](LICENSE) © Misja Pronk
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## How Isolinear handles secrets
|
|
4
|
+
|
|
5
|
+
- **Secret values are never persisted.** They're read on demand via the
|
|
6
|
+
Databricks SDK (`get_secret`) and held only in memory for the current session.
|
|
7
|
+
- **Reveal is lazy.** Values are fetched only when you explicitly reveal or copy
|
|
8
|
+
one — they are not bulk-pulled during the startup cache warm.
|
|
9
|
+
- **Saved profiles store no secrets.** When you save a connection, Isolinear
|
|
10
|
+
writes only a `host` and `auth_type = external-browser` to `~/.databrickscfg`
|
|
11
|
+
(the same thing `databricks auth login` does). Authentication is delegated to
|
|
12
|
+
the Databricks SDK's unified auth / OAuth token cache.
|
|
13
|
+
- **The SDK boundary is isolated.** Only the `infrastructure/` layer touches the
|
|
14
|
+
Databricks SDK or the network.
|
|
15
|
+
|
|
16
|
+
Copying a value places it on your system clipboard — clear it afterwards if you
|
|
17
|
+
share your machine.
|
|
18
|
+
|
|
19
|
+
## Supported versions
|
|
20
|
+
|
|
21
|
+
The latest released version on PyPI is supported. Please upgrade before
|
|
22
|
+
reporting an issue.
|
|
23
|
+
|
|
24
|
+
## Reporting a vulnerability
|
|
25
|
+
|
|
26
|
+
Please report security issues **privately**:
|
|
27
|
+
|
|
28
|
+
- Open a [GitHub Security Advisory](https://github.com/misja-pronk/isolinear/security/advisories/new), or
|
|
29
|
+
- email **misja@prorexconsultancy.nl**.
|
|
30
|
+
|
|
31
|
+
Do not open a public issue for security reports. You'll get an acknowledgement
|
|
32
|
+
as soon as possible, and we'll coordinate a fix and disclosure with you.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Isolinear — Databricks Secret Manager · User Stories
|
|
2
|
+
|
|
3
|
+
A keyboard-driven terminal app for managing Databricks secrets. Superfile-inspired
|
|
4
|
+
multi-pane layout with its own distinct "Isolinear" theme. Built with Python, Textual,
|
|
5
|
+
and the Databricks SDK.
|
|
6
|
+
|
|
7
|
+
> **Workspace** in this app = a connection profile in `~/.databrickscfg`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Epic 1 — Workspace connection & navigation
|
|
12
|
+
| ID | Story | Acceptance |
|
|
13
|
+
|----|-------|-----------|
|
|
14
|
+
| US-1 | See all configured workspaces (profiles) and pick one. | Profiles parsed from `~/.databrickscfg`; host shown; selectable on the login hub. |
|
|
15
|
+
| US-1a | Sign in with a workspace URL when no profile exists. | Paste host → OAuth U2M browser login (`auth_type=external-browser`); no token. |
|
|
16
|
+
| US-1b | Discover workspaces via the account (AWS/Azure/GCP). | Pick cloud + account ID → account OAuth → `workspaces.list()` → pick → `get_workspace_client`. |
|
|
17
|
+
| US-1c | Persist a login as a reusable profile. | Optional "save as" writes host+auth_type to `~/.databrickscfg`; instant next launch. |
|
|
18
|
+
| US-2 | Connection verified; shows authenticated identity. | `current_user.me()` resolved; username + status shown. |
|
|
19
|
+
| US-3 | Switch workspaces without restarting. | `w` opens the login hub; switching re-warms cache for new workspace. |
|
|
20
|
+
|
|
21
|
+
## Epic 2 — Browsing scopes & secrets
|
|
22
|
+
| ID | Story | Acceptance |
|
|
23
|
+
|----|-------|-----------|
|
|
24
|
+
| US-4 | Browse all scopes with backend type. | Scopes list shows `DATABRICKS` vs `AZURE_KEYVAULT`. |
|
|
25
|
+
| US-5 | Browse secret keys in a scope with timestamps. | Keys + last-updated shown for selected scope. |
|
|
26
|
+
| US-6 | Detail panel for selected secret/scope. | Right pane shows metadata + ACLs. |
|
|
27
|
+
|
|
28
|
+
## Epic 3 — Secret operations (CRUD)
|
|
29
|
+
| ID | Story | Acceptance |
|
|
30
|
+
|----|-------|-----------|
|
|
31
|
+
| US-7 | Create a secret (key + value) in a scope. | `put_secret`; cache + list update. |
|
|
32
|
+
| US-8 | Update an existing secret's value. | `put_secret` on existing key; timestamp refreshes. |
|
|
33
|
+
| US-9 | Delete a secret with confirmation. | Confirm modal; `delete_secret`; row removed. |
|
|
34
|
+
| US-10 | Reveal & copy a secret value. | `get_secret`; reveal toggle; copy to clipboard. |
|
|
35
|
+
| US-11 | Create & delete scopes. | `create_scope` / `delete_scope` with confirm. |
|
|
36
|
+
| US-11a | Update a scope's permissions (ACLs). | `p` opens a permissions editor; `put_acl` / `delete_acl` (a scope is otherwise immutable). |
|
|
37
|
+
|
|
38
|
+
## Epic 4 — Authorization overview
|
|
39
|
+
| ID | Story | Acceptance |
|
|
40
|
+
|----|-------|-----------|
|
|
41
|
+
| US-12 | See & edit ACLs (principal + permission) per scope. | `list_acls` in detail pane; full add/change/remove via the `p` editor. |
|
|
42
|
+
| US-13 | Authorization status overview. | My identity + my effective permission per scope; write-capable flagged. |
|
|
43
|
+
|
|
44
|
+
## Epic 5 — Performance & UX (caching / pre-call)
|
|
45
|
+
| ID | Story | Acceptance |
|
|
46
|
+
|----|-------|-----------|
|
|
47
|
+
| US-14 | Pre-load scopes, secret metadata, ACLs on startup. | Background worker warms cache; progress shown. |
|
|
48
|
+
| US-15 | Refresh cache on demand. | `r` refreshes scope; `R` refreshes workspace. |
|
|
49
|
+
| US-16 | Lazy + cached secret values. | Values fetched only on reveal; cached thereafter. |
|
|
50
|
+
|
|
51
|
+
## Epic 6 — Look & feel
|
|
52
|
+
| ID | Story | Acceptance |
|
|
53
|
+
|----|-------|-----------|
|
|
54
|
+
| US-17 | Distinct "Isolinear" theme. | Violet-cyber LCARS palette, isolinear-chip motif, multi-pane. |
|
|
55
|
+
| US-18 | Keyboard-driven with footer + help overlay. | Footer bindings; `?` help screen. |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Out of scope (v1 / future)
|
|
60
|
+
- Account-level workspace enumeration (requires account auth).
|
|
61
|
+
- Secret value history / versioning (not exposed by the API).
|
|
62
|
+
- Bulk import/export of secrets.
|
|
63
|
+
- Renaming a scope (immutable in the Databricks API — delete + recreate).
|