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.
Files changed (59) hide show
  1. isolinear-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +41 -0
  2. isolinear-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. isolinear-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  4. isolinear-0.1.0/.github/workflows/ci.yml +59 -0
  5. isolinear-0.1.0/.github/workflows/release.yml +43 -0
  6. isolinear-0.1.0/.gitignore +8 -0
  7. isolinear-0.1.0/CHANGELOG.md +27 -0
  8. isolinear-0.1.0/CONTRIBUTING.md +62 -0
  9. isolinear-0.1.0/LICENSE +21 -0
  10. isolinear-0.1.0/PKG-INFO +163 -0
  11. isolinear-0.1.0/README.md +132 -0
  12. isolinear-0.1.0/SECURITY.md +32 -0
  13. isolinear-0.1.0/docs/USER_STORIES.md +63 -0
  14. isolinear-0.1.0/docs/img/auth.png +0 -0
  15. isolinear-0.1.0/docs/img/browse.png +0 -0
  16. isolinear-0.1.0/docs/img/login.png +0 -0
  17. isolinear-0.1.0/docs/img/perms.png +0 -0
  18. isolinear-0.1.0/mise.toml +7 -0
  19. isolinear-0.1.0/pyproject.toml +87 -0
  20. isolinear-0.1.0/src/isolinear/__init__.py +0 -0
  21. isolinear-0.1.0/src/isolinear/__main__.py +4 -0
  22. isolinear-0.1.0/src/isolinear/app.py +71 -0
  23. isolinear-0.1.0/src/isolinear/application/__init__.py +16 -0
  24. isolinear-0.1.0/src/isolinear/application/onboarding.py +64 -0
  25. isolinear-0.1.0/src/isolinear/application/read_model.py +73 -0
  26. isolinear-0.1.0/src/isolinear/application/workspace.py +141 -0
  27. isolinear-0.1.0/src/isolinear/domain/__init__.py +46 -0
  28. isolinear-0.1.0/src/isolinear/domain/errors.py +12 -0
  29. isolinear-0.1.0/src/isolinear/domain/host.py +15 -0
  30. isolinear-0.1.0/src/isolinear/domain/models.py +102 -0
  31. isolinear-0.1.0/src/isolinear/domain/permissions.py +58 -0
  32. isolinear-0.1.0/src/isolinear/domain/ports.py +50 -0
  33. isolinear-0.1.0/src/isolinear/domain/secret_store.py +30 -0
  34. isolinear-0.1.0/src/isolinear/infrastructure/__init__.py +18 -0
  35. isolinear-0.1.0/src/isolinear/infrastructure/config.py +13 -0
  36. isolinear-0.1.0/src/isolinear/infrastructure/connector.py +113 -0
  37. isolinear-0.1.0/src/isolinear/infrastructure/databricks.py +154 -0
  38. isolinear-0.1.0/src/isolinear/infrastructure/profiles.py +66 -0
  39. isolinear-0.1.0/src/isolinear/interface/__init__.py +0 -0
  40. isolinear-0.1.0/src/isolinear/interface/modals.py +360 -0
  41. isolinear-0.1.0/src/isolinear/interface/screens/__init__.py +0 -0
  42. isolinear-0.1.0/src/isolinear/interface/screens/login.py +292 -0
  43. isolinear-0.1.0/src/isolinear/interface/screens/main.py +591 -0
  44. isolinear-0.1.0/src/isolinear/interface/theme.py +84 -0
  45. isolinear-0.1.0/src/isolinear/interface/widgets.py +321 -0
  46. isolinear-0.1.0/src/isolinear/styles.tcss +260 -0
  47. isolinear-0.1.0/tests/conftest.py +24 -0
  48. isolinear-0.1.0/tests/fakes.py +147 -0
  49. isolinear-0.1.0/tests/test_cache.py +35 -0
  50. isolinear-0.1.0/tests/test_databricks.py +107 -0
  51. isolinear-0.1.0/tests/test_host.py +22 -0
  52. isolinear-0.1.0/tests/test_models.py +48 -0
  53. isolinear-0.1.0/tests/test_permissions.py +23 -0
  54. isolinear-0.1.0/tests/test_profiles.py +76 -0
  55. isolinear-0.1.0/tests/test_session.py +87 -0
  56. isolinear-0.1.0/tests/test_ui_browse.py +126 -0
  57. isolinear-0.1.0/tests/test_ui_login.py +36 -0
  58. isolinear-0.1.0/tests/test_ui_permissions.py +65 -0
  59. 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,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/misja-pronk/isolinear/security/advisories/new
5
+ about: Please report security issues privately, not as a public issue.
@@ -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,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .textual/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .DS_Store
@@ -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).
@@ -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.
@@ -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
+ [![ci](https://github.com/misja-pronk/isolinear/actions/workflows/ci.yml/badge.svg)](https://github.com/misja-pronk/isolinear/actions/workflows/ci.yml)
39
+ [![PyPI](https://img.shields.io/pypi/v/isolinear.svg)](https://pypi.org/project/isolinear/)
40
+ [![Python](https://img.shields.io/pypi/pyversions/isolinear.svg)](https://pypi.org/project/isolinear/)
41
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
42
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
43
+
44
+ ![Isolinear browsing secrets](docs/img/browse.png)
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
+ [![ci](https://github.com/misja-pronk/isolinear/actions/workflows/ci.yml/badge.svg)](https://github.com/misja-pronk/isolinear/actions/workflows/ci.yml)
8
+ [![PyPI](https://img.shields.io/pypi/v/isolinear.svg)](https://pypi.org/project/isolinear/)
9
+ [![Python](https://img.shields.io/pypi/pyversions/isolinear.svg)](https://pypi.org/project/isolinear/)
10
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
11
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
12
+
13
+ ![Isolinear browsing secrets](docs/img/browse.png)
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).