tokentoss 0.1.0__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/workflows/release.yml +1 -1
  2. {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/workflows/start-release.yml +8 -2
  3. {tokentoss-0.1.0 → tokentoss-0.1.1}/PKG-INFO +1 -1
  4. tokentoss-0.1.1/_blueprint/plan/01-next-steps.md +38 -0
  5. {tokentoss-0.1.0 → tokentoss-0.1.1}/pyproject.toml +1 -1
  6. tokentoss-0.1.0/_blueprint/ideas/nicholasgrundl-site-deployment.md +0 -595
  7. tokentoss-0.1.0/_blueprint/plan/01-next-steps.md +0 -46
  8. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/agents/docs-updater.md +0 -0
  9. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/commit-auto.md +0 -0
  10. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/commit-macro.md +0 -0
  11. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/commit-manual.md +0 -0
  12. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/design-plan.md +0 -0
  13. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/design-review.md +0 -0
  14. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/tasks-align.md +0 -0
  15. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/tasks-implement.md +0 -0
  16. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/work-archive.md +0 -0
  17. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/work-status.md +0 -0
  18. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/hooks/commit-auto.sh +0 -0
  19. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/hooks/macro-commit.sh +0 -0
  20. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/hooks/micro-commit.sh +0 -0
  21. {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/hooks/templates/macro-commit-prompt.txt +0 -0
  22. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/commands/commit/auto.toml +0 -0
  23. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/commands/commit/macro.toml +0 -0
  24. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/commands/commit/manual.toml +0 -0
  25. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/hooks/commit-auto.sh +0 -0
  26. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/hooks/macro-commit.sh +0 -0
  27. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/hooks/micro-commit.sh +0 -0
  28. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/hooks/templates/macro-commit-prompt.txt +0 -0
  29. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/settings.json +0 -0
  30. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/skills/docs-updater/SKILL.md +0 -0
  31. {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  32. {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  33. {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/workflows/ci.yml +0 -0
  34. {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/workflows/security.yml +0 -0
  35. {tokentoss-0.1.0 → tokentoss-0.1.1}/.gitignore +0 -0
  36. {tokentoss-0.1.0 → tokentoss-0.1.1}/CLAUDE.md +0 -0
  37. {tokentoss-0.1.0 → tokentoss-0.1.1}/CONTRIBUTING.md +0 -0
  38. {tokentoss-0.1.0 → tokentoss-0.1.1}/LICENSE +0 -0
  39. {tokentoss-0.1.0 → tokentoss-0.1.1}/README.md +0 -0
  40. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/CLAUDE.md +0 -0
  41. {tokentoss-0.1.0/_blueprint/plan → tokentoss-0.1.1/_blueprint/archive}/04-distribution-pypi.md +0 -0
  42. {tokentoss-0.1.0/_blueprint/plan → tokentoss-0.1.1/_blueprint/archive}/05-ci-cd-github-actions.md +0 -0
  43. {tokentoss-0.1.0/_blueprint/plan → tokentoss-0.1.1/_blueprint/archive}/08-manual-testing.md +0 -0
  44. {tokentoss-0.1.0/_blueprint/implement → tokentoss-0.1.1/_blueprint/archive}/A-github-actions-setup.md +0 -0
  45. {tokentoss-0.1.0/_blueprint/implement → tokentoss-0.1.1/_blueprint/archive}/B-initial-release-plan.md +0 -0
  46. {tokentoss-0.1.0/_blueprint/implement → tokentoss-0.1.1/_blueprint/archive}/RELEASE-1-merge-and-publish.md +0 -0
  47. {tokentoss-0.1.0/_blueprint/implement → tokentoss-0.1.1/_blueprint/archive}/RELEASE-2-v010-pypi.md +0 -0
  48. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/bugfix-implementation.md +0 -0
  49. {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/ci-cd-notes.md +0 -0
  50. {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/cleanup-notes.md +0 -0
  51. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/client_secrets_implementation.md +0 -0
  52. {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-concept.md +0 -0
  53. {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-outline-claude.md +0 -0
  54. {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-outline-codex.md +0 -0
  55. {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-outline-gemini.md +0 -0
  56. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/phase-2-implementation.md +0 -0
  57. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/phase-3-implementation.md +0 -0
  58. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/phase-5-polish-implementation.md +0 -0
  59. {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/plan-v1.md +0 -0
  60. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/polish-improvement-implementation.md +0 -0
  61. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/progress.md +0 -0
  62. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/testing-improvement-implementation.md +0 -0
  63. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/tokentoss_implementation.md +0 -0
  64. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/widget-testing-feedback.md +0 -0
  65. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/README.md +0 -0
  66. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/agent-skills.md +0 -0
  67. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/custom-commands.md +0 -0
  68. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/headless-mode.md +0 -0
  69. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/hooks-reference.md +0 -0
  70. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/hooks.md +0 -0
  71. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/sandboxing.md +0 -0
  72. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/settings.md +0 -0
  73. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/sub-agents.md +0 -0
  74. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/system-prompt-override.md +0 -0
  75. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/google-iap/README.md +0 -0
  76. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/platformdirs/README.md +0 -0
  77. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/platformdirs/directory-types.md +0 -0
  78. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/platformdirs/platformdirs-overview.md +0 -0
  79. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest/pytest-general-foundations.md +0 -0
  80. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest/pytest-project-guidance.md +0 -0
  81. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest-mock/configuration.md +0 -0
  82. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest-mock/overview.md +0 -0
  83. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest-mock/remarks.md +0 -0
  84. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest-mock/usage.md +0 -0
  85. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ruff/README.md +0 -0
  86. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/01-overview.md +0 -0
  87. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/02-github-readme.md +0 -0
  88. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/03-configuration.md +0 -0
  89. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/04-cli-reference.md +0 -0
  90. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/05-editors.md +0 -0
  91. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/06-configuration-reference.md +0 -0
  92. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/07-environment-variables.md +0 -0
  93. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/README.md +0 -0
  94. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/implement/C-release-implementation.md +0 -0
  95. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/implement/D-test-service-gcp-setup.md +0 -0
  96. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/implement/E-widget-testing-guide.md +0 -0
  97. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/plan/02-architectural-analysis.md +0 -0
  98. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/plan/03-widgets-subpackage.md +0 -0
  99. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/plan/06-test-service-cloud-run.md +0 -0
  100. {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/plan/07-docs-and-tutorials.md +0 -0
  101. {tokentoss-0.1.0 → tokentoss-0.1.1}/docs/gcp-admin-setup.md +0 -0
  102. {tokentoss-0.1.0 → tokentoss-0.1.1}/docs/quickstart.md +0 -0
  103. {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/Dev-prerelease-testing.ipynb +0 -0
  104. {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/Widget-Testing.ipynb +0 -0
  105. {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/test-service/Dockerfile +0 -0
  106. {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/test-service/README.md +0 -0
  107. {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/test-service/main.py +0 -0
  108. {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/test-service/requirements.txt +0 -0
  109. {tokentoss-0.1.0 → tokentoss-0.1.1}/justfile +0 -0
  110. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/__init__.py +0 -0
  111. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/_logging.py +0 -0
  112. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/_telemetry.py +0 -0
  113. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/auth_manager.py +0 -0
  114. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/client.py +0 -0
  115. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/configure_widget.py +0 -0
  116. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/exceptions.py +0 -0
  117. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/setup.py +0 -0
  118. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/storage.py +0 -0
  119. {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/widget.py +0 -0
  120. {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/__init__.py +0 -0
  121. {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_auth_manager.py +0 -0
  122. {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_client.py +0 -0
  123. {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_configure_widget.py +0 -0
  124. {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_setup.py +0 -0
  125. {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_storage.py +0 -0
  126. {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_widget.py +0 -0
  127. {tokentoss-0.1.0 → tokentoss-0.1.1}/uv.lock +0 -0
@@ -41,7 +41,7 @@ jobs:
41
41
  - uses: pypa/gh-action-pypi-publish@release/v1
42
42
 
43
43
  github-release:
44
- needs: build
44
+ needs: publish
45
45
  runs-on: ubuntu-latest
46
46
  permissions:
47
47
  contents: write
@@ -11,13 +11,19 @@ on:
11
11
  jobs:
12
12
  release:
13
13
  runs-on: ubuntu-latest
14
- permissions:
15
- contents: write
16
14
  steps:
15
+ - name: Generate app token
16
+ id: app-token
17
+ uses: actions/create-github-app-token@v1
18
+ with:
19
+ app-id: ${{ vars.RELEASE_APP_ID }}
20
+ private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
21
+
17
22
  - uses: actions/checkout@v4
18
23
  with:
19
24
  ref: main
20
25
  fetch-tags: true
26
+ token: ${{ steps.app-token.outputs.token }}
21
27
 
22
28
  - name: Validate version format
23
29
  run: |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tokentoss
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: OAuth authentication from Jupyter notebooks for IAP-protected GCP services
5
5
  Project-URL: Homepage, https://github.com/NicholasGrundl/tokentoss
6
6
  Project-URL: Repository, https://github.com/NicholasGrundl/tokentoss
@@ -0,0 +1,38 @@
1
+ # Next Steps — Post v0.1.0
2
+
3
+ v0.1.0 is released on PyPI and GitHub. This document tracks remaining work for future releases.
4
+
5
+ ## Completed (v0.1.0)
6
+
7
+ - **Distribution:** Public PyPI (`pip install tokentoss`) — live at https://pypi.org/project/tokentoss/
8
+ - **CI/CD:** GitHub Actions with tag-triggered releases via PyPI trusted publishers (OIDC)
9
+ - **Manual testing:** Pre-release widget testing in JupyterLab
10
+ - **GitHub settings:** Branch protection, required status checks, squash-only merges
11
+
12
+ Archived plans: 04 (distribution), 05 (CI/CD), 08 (manual testing)
13
+
14
+ ---
15
+
16
+ ## Remaining Plan Documents (v0.2.0+)
17
+
18
+ ### 02 — Architectural Analysis
19
+ Observations on current limitations (detached token refresh, global state management, storage security) and potential improvements for robustness and scalability.
20
+
21
+ ### 03 — Widgets Subpackage
22
+ Refactor `widget.py` and `configure_widget.py` into a `widgets/` subpackage. Detailed migration steps, import changes, and an optional future split for `CallbackServer`.
23
+
24
+ ### 06 — Test Service (Cloud Run + IAP)
25
+ A FastAPI microservice deployed behind IAP on Cloud Run to verify tokens end-to-end and demonstrate user-specific content. Full GCP setup walkthrough from zero.
26
+
27
+ ### 07 — Documentation & Tutorials
28
+ Three-tier doc strategy: Tier 1 (quick-start guide + GCP admin setup), Tier 2 (API reference + example notebooks), Tier 3 (hosted docs site, troubleshooting guide). Tier 1 is the next priority.
29
+
30
+ ---
31
+
32
+ ## Suggested Execution Order
33
+
34
+ 1. **Docs Tier 1** (07) — quick-start guide and GCP admin setup
35
+ 2. **Widgets refactor** (03) — code cleanup before wider adoption
36
+ 3. **Test service** (06) — deploy verification endpoint
37
+ 4. **Architectural improvements** (02) — address technical debt
38
+ 5. **Docs Tiers 2-3** (07) — API reference, hosted docs
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tokentoss"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  description = "OAuth authentication from Jupyter notebooks for IAP-protected GCP services"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,595 +0,0 @@
1
- # Complete Deployment Guide: 0 to 100
2
-
3
- *Step-by-step instructions for deploying Astro portfolio site to Digital Ocean*
4
-
5
- ---
6
-
7
- ## Prerequisites
8
-
9
- Before starting:
10
- - [ ] Digital Ocean account with payment method
11
- - [ ] Domain name registered (nicholasgrundl.com)
12
- - [ ] Local machine with Node.js 18+, npm, Docker
13
- - [ ] Tailscale account (free)
14
- - [ ] Astro project built locally (`npm run build` works)
15
- - [ ] Create `.env` file from `.env.example` with your droplet IP addresses
16
-
17
- > **Note on Docker Build**: This guide uses legacy `docker build` commands. You may see a deprecation warning about buildx - this can be safely ignored. The production VM has buildx installed, and we can migrate anytime without breaking changes. See [Appendix: Migrating to Docker Buildx](#appendix-migrating-to-docker-buildx) for migration instructions.
18
-
19
- ---
20
-
21
- ## TLDR: Abridged Steps
22
-
23
- 0. Setup local environment
24
- - copy `.env.example` to `.env` (you'll fill in IPs later)
25
-
26
- 1. Create Droplet
27
- - obtain public IP address
28
-
29
- 2. Configure droplet OS
30
- - install tailscale -> obtain tailscale IP address
31
- - **update `.env` file with both IP addresses**
32
- - install caddy
33
- - docker, docker-compose
34
-
35
-
36
- 3. Deploy files to droplet
37
- - transfer docker image
38
- - transfer caddyfile
39
- - transfer docker-compose
40
-
41
- 4. Launch website
42
- - reload caddy
43
- - start docker-compose
44
-
45
- 5. Configure DNS
46
- - connect to droplet IP address
47
-
48
-
49
-
50
- ## Part 1: Infrastructure Setup (Completed)
51
-
52
- ### ✅ Step 1: Create Digital Ocean Project
53
-
54
- - Project name: `personal-website`
55
- - All resources will be organized under this project
56
-
57
- ### ✅ Step 2: Configure Firewalls
58
-
59
- We create **two reusable firewalls** for different server types:
60
-
61
- > **Why two firewalls?**
62
- > - Separation of concerns: public vs internal servers
63
- > - Reusable patterns for future infrastructure
64
- > - Easier to manage security rules per service type
65
-
66
- #### Firewall 1: `http-https` (For Public Web Servers)
67
-
68
- **Use case**: Servers that need to serve HTTP/HTTPS traffic (web servers, APIs, reverse proxies)
69
-
70
- **Inbound rules**:
71
- - SSH (22) - All IPs (will be locked to Tailscale later in Security Hardening)
72
- - HTTP (80) - All IPs
73
- - HTTPS (443) - All IPs
74
- - Tailscale UDP (41641) - All IPs
75
-
76
-
77
- #### Firewall 2: `no-inbound` (For Backend/Internal Servers)
78
-
79
- **Use case**: Servers that should NOT be publicly accessible (databases, Redis, internal APIs, background workers)
80
-
81
- **Inbound rules**:
82
- - SSH (22) - Tailscale subnet only (100.0.0.0/8)
83
- - Tailscale UDP (41641) - All IPs
84
-
85
-
86
- ### ✅ Step 3: Launch Ubuntu Droplet
87
-
88
- - Hostname: `web01`
89
- - OS: Ubuntu 24.04 LTS
90
- - Firewall: `http-https` attached
91
- - Monitoring: Enabled
92
- - Public IP: [DROPLET_PUBLIC_IP]
93
-
94
- ### ✅ Step 4: Install and Configure Tailscale
95
-
96
- **On droplet:**
97
- ```bash
98
- ssh root@DROPLET_PUBLIC_IP
99
- curl -fsSL https://tailscale.com/install.sh | sh
100
- sudo tailscale up
101
- tailscale ip -4
102
- # Note the Tailscale IP: 100.x.x.x -> DROPLET_TAILSCALE_IP
103
- ```
104
-
105
- > **On local machine:**
106
- > - Installed Tailscale desktop app
107
- > - Authenticated with same account
108
- > - Can SSH via Tailscale IP: `ssh root@DROPLET_TAILSCALE_IP`
109
-
110
- ### Step 4.1: Create .env File (On Local Machine)
111
-
112
- Now that you have both IP addresses, create your local `.env` file for deployment automation:
113
-
114
- ```bash
115
- # On local machine, in project directory
116
- # Copy the example file
117
- cp .env.example .env
118
-
119
- # Edit .env and add your IP addresses
120
- # DROPLET_PUBLIC_IP=206.xxx.xx.xxx # Replace with your droplet's public IP
121
- # DROPLET_TAILSCALE_IP=100.xx.xxx.x # Replace with your droplet's Tailscale IP
122
- ```
123
-
124
- **Verify your .env file contains:**
125
- ```bash
126
- cat .env
127
- # Should show:
128
- # DROPLET_PUBLIC_IP=your_actual_public_ip
129
- # DROPLET_TAILSCALE_IP=your_actual_droplet_tailscale_ip
130
- ```
131
-
132
- **Note**: The `.env` file is git-ignored and will never be committed. This keeps your infrastructure details secure.
133
-
134
- ### ✅ Step 5: Install Caddy
135
-
136
-
137
- ```bash
138
- ssh root@DROPLET_TAILSCALE_IP
139
-
140
- sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
141
- curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
142
- sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
143
- curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
144
- sudo tee /etc/apt/sources.list.d/caddy-stable.list
145
- sudo apt update
146
- sudo apt install caddy
147
-
148
- # Verify
149
- sudo systemctl status caddy
150
- ```
151
-
152
- **Note**: Caddy is now installed but not yet configured. The `Caddyfile` lives in your project repository and will be copied to the droplet during deployment (Step 9).
153
-
154
- ---
155
-
156
- ## Part 2: Docker Setup
157
-
158
- ### Step 6: Install Docker and Docker Compose
159
-
160
- ```bash
161
- ssh root@DROPLET_TAILSCALE_IP
162
-
163
- # Update package index
164
- sudo apt update
165
-
166
- # Install prerequisites
167
- sudo apt install -y ca-certificates curl gnupg
168
-
169
- # Add Docker's GPG key
170
- sudo install -m 0755 -d /etc/apt/keyrings
171
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
172
- sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
173
- sudo chmod a+r /etc/apt/keyrings/docker.gpg
174
-
175
- # Add Docker repository
176
- echo \
177
- "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
178
- $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
179
- sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
180
-
181
- # Install Docker
182
- sudo apt update
183
- sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
184
-
185
- # Verify installation
186
- docker --version
187
- docker compose version
188
-
189
- # Test Docker
190
- docker run hello-world
191
- ```
192
-
193
- **Expected output:**
194
- ```
195
- Docker version 24.x.x
196
- Docker Compose version v2.x.x
197
- Hello from Docker! [success message]
198
- ```
199
-
200
- ### Step 7: Create Application Directory
201
-
202
- ```bash
203
- ssh root@DROPLET_TAILSCALE_IP
204
-
205
- #on droplet
206
- mkdir -p /opt/personal-website
207
- cd /opt/personal-website
208
- ```
209
-
210
- ---
211
-
212
- ## Part 3: Application Deployment
213
-
214
- ### Step 8: Build and Deploy
215
-
216
- > **These files should already exist in your repository/local machine:**
217
- > `Dockerfile`
218
- > `docker-compose.yaml`
219
- > `.dockerignore`
220
- > `Caddyfile`
221
- > `astro.config.mjs` and `src/` code for building astro site
222
-
223
- We manually deploy to the droplet from our local machine
224
-
225
- ```bash
226
- # On local machine, in project directory
227
-
228
- # Build Docker image (this also runs 'npm run build' inside the container) (just docker-build)
229
- docker build -t personal-website:latest .
230
-
231
- # Transfer image to droplet (via Tailscale) (just deploy-transfer)
232
- docker save personal-website:latest | gzip | ssh root@DROPLET_TAILSCALE_IP "gunzip | docker load"
233
-
234
- # Transfer configuration files (just deploy-config)
235
- scp docker-compose.yml root@DROPLET_TAILSCALE_IP:/opt/personal-website/
236
- scp Caddyfile root@DROPLET_TAILSCALE_IP:/etc/caddy/Caddyfile
237
-
238
- # Reload Caddy with new configuration (part of just deploy-caddy)
239
- ssh root@DROPLET_TAILSCALE_IP "sudo systemctl reload caddy"
240
-
241
- # Start container on droplet (just deploy-restart)
242
- ssh root@DROPLET_TAILSCALE_IP "cd /opt/personal-website && docker compose up -d"
243
-
244
- # Check status (just deploy-status)
245
- ssh root@DROPLET_TAILSCALE_IP "docker compose -f /opt/personal-website/docker-compose.yml ps"
246
-
247
- # View logs (just logs-follow)
248
- ssh root@DROPLET_TAILSCALE_IP "docker compose -f /opt/personal-website/docker-compose.yml logs -f"
249
- ```
250
-
251
- **Expected output:**
252
- ```
253
- NAME IMAGE STATUS PORTS
254
- web personal-website:latest Up 10 seconds 127.0.0.1:4321->4321/tcp
255
- ```
256
-
257
- ### Step 9: Test Locally on Droplet
258
-
259
- ```bash
260
- ssh root@DROPLET_TAILSCALE_IP
261
-
262
- # Test Node.js server responds
263
- curl -I http://localhost:4321
264
-
265
- # Expected: HTTP/1.1 200 OK
266
-
267
- # Test Caddy proxy
268
- curl -I -H "Host: nicholasgrundl.com" http://localhost
269
-
270
- # Expected: HTTP/1.1 308 Permanent Redirect to https://nicholasgrundl.com/
271
- # This is good! Caddy is correctly redirecting HTTP to HTTPS and to the canonical domain.
272
- ```
273
-
274
- ---
275
-
276
- ## Part 4: DNS Configuration
277
-
278
- ### Step 10: Configure DNS for Caddy & Cloudflare
279
-
280
- This is a critical step to get Caddy's automatic HTTPS working correctly with Cloudflare's proxy. We will point the DNS to the droplet, let Caddy get a certificate, and then enable the Cloudflare proxy.
281
-
282
- **Assumptions:**
283
- - Your domain `nicholasgrundl.com` is registered with or managed by Cloudflare.
284
- - You are in the "DNS" settings for your domain in the Cloudflare dashboard.
285
-
286
- #### Part A: Initial DNS Setup (DNS Only)
287
-
288
- First, we create the records but leave them in "DNS only" mode. This allows Caddy to get a certificate from Let's Encrypt.
289
-
290
- 1. **Clean up old records:** Delete any existing `A`, `AAAA`, or `CNAME` records for `nicholasgrundl.com` and `www` that might be left over from a previous host.
291
-
292
- 2. **Create an `A` record for the root domain:**
293
- * **Type**: `A`
294
- * **Name**: `@`
295
- * **IPv4 address**: Your droplet's public IP (`[DROPLET_PUBLIC_IP]`)
296
- * **Proxy status**: **DNS only** (grey cloud)
297
- * **TTL**: Auto
298
-
299
- 3. **Create a `CNAME` record for `www`:**
300
- * **Type**: `CNAME`
301
- * **Name**: `www`
302
- * **Target**: `nicholasgrundl.com`
303
- * **Proxy status**: **DNS only** (grey cloud)
304
- * **TTL**: Auto
305
-
306
- At this point, your DNS is pointing directly to your server.
307
-
308
- #### Part B: Verify Caddy Certificate Acquisition
309
-
310
- 1. **Ensure Caddy is running:** The deployment steps in Part 3 should have already started Caddy. If not, SSH into your droplet and run `sudo systemctl reload caddy`.
311
- 2. **Wait for propagation:** Wait 1-2 minutes for the DNS changes to be visible globally.
312
- 3. **Check Caddy logs:** Watch the Caddy logs for the certificate acquisition message.
313
- ```bash
314
- ssh root@DROPLET_TAILSCALE_IP "sudo journalctl -u caddy -f"
315
- # Or using just: just logs-follow
316
- # Look for messages like "certificate obtained successfully"
317
- ```
318
- 4. **Verify HTTPS:** From your local machine, test that the site is secure.
319
- ```bash
320
- curl -I https://nicholasgrundl.com
321
- # You should see "HTTP/2 200"
322
- ```
323
- You can also open the site in a browser and check for the lock icon.
324
-
325
- #### Part C: Enable Cloudflare Proxy (Orange Cloud)
326
-
327
- Once you've confirmed your site is working over HTTPS directly, you can enable Cloudflare's proxy to get the performance and security benefits.
328
-
329
- 1. **Change Proxy Status:** Go back to your Cloudflare DNS settings. Edit the `A` and `CNAME` records and change their **Proxy status** to **Proxied (orange cloud)**.
330
-
331
- 2. **Set SSL/TLS Mode:** In the Cloudflare dashboard, go to the **SSL/TLS** tab for your domain. Set the encryption mode to **Full (Strict)**. This is the most secure option and is required for end-to-end encryption with your Caddy server.
332
-
333
- Your site is now fully configured and running through Cloudflare.
334
-
335
- ### Step 11: Verify Final DNS Configuration
336
-
337
- After enabling the Cloudflare proxy, you can verify that DNS is now pointing to Cloudflare's servers, which is the correct final state.
338
-
339
- ```bash
340
- # Check DNS from local machine
341
- dig nicholasgrundl.com
342
- ```
343
-
344
- **Expected output (after propagation):**
345
- The `ANSWER SECTION` should now show one or more `A` records pointing to **Cloudflare's IP addresses**, not your droplet's IP. This confirms that traffic is being proxied.
346
-
347
- ```
348
- ;; ANSWER SECTION:
349
- nicholasgrundl.com. 300 IN A 104.21.x.x
350
- nicholasgrundl.com. 300 IN A 172.67.x.x
351
- ```
352
-
353
- You can also check the `www` subdomain:
354
- ```bash
355
- dig www.nicholasgrundl.com
356
- # This should show a CNAME record pointing to nicholasgrundl.com,
357
- # and then the same Cloudflare IP addresses.
358
- ```
359
-
360
- **Check propagation**: You can use a tool like https://dnschecker.org to see your DNS records from multiple locations around the world.
361
-
362
- ### Step 12: Verify HTTPS Works
363
-
364
- **After DNS propagates:**
365
-
366
- ```bash
367
- # From local machine
368
- curl -I https://nicholasgrundl.com
369
-
370
- # Expected: HTTP/2 200
371
-
372
- # Check certificate
373
- curl -vI https://nicholasgrundl.com 2>&1 | grep -i issuer
374
- # Expected: Let's Encrypt
375
- ```
376
-
377
- **In browser:**
378
- - Navigate to: https://nicholasgrundl.com
379
- - Check for padlock icon (secure connection)
380
- - Open DevTools → Console (should be no errors)
381
- - Verify CSS/JS/images all loaded
382
-
383
- **Monitor Caddy during first HTTPS request:**
384
- ```bash
385
- ssh root@DROPLET_TAILSCALE_IP
386
- sudo journalctl -u caddy -f
387
- # Or using just: just logs-follow
388
-
389
- # Watch for:
390
- # "certificate obtained successfully"
391
- ```
392
-
393
- ---
394
-
395
- ## Part 5: Ongoing Maintenance
396
-
397
- This section covers routine tasks to keep your deployment healthy and secure.
398
-
399
- ### Periodic System Updates
400
-
401
- It's good practice to periodically update the packages on your droplet to receive security patches and bug fixes.
402
-
403
- ```bash
404
- # SSH into your droplet and run:
405
- ssh root@{{DROPLET_TAILSCALE_IP}} "sudo apt update && sudo apt upgrade -y && docker system prune -f"
406
- ```
407
-
408
- ### Manual SSL Certificate Renewal (Every ~60-80 Days)
409
-
410
- > **Note:** This manual process is required because the Cloudflare proxy interferes with Caddy's default renewal process. For a permanent, automated solution, see "Automate SSL Certificate Renewal" in the "Future Improvements" section.
411
-
412
- Let's Encrypt certificates are valid for 90 days. You should renew them before they expire.
413
-
414
- 1. **Disable Cloudflare Proxy:**
415
- * In your Cloudflare DNS settings, change the `A` record for `@` and the `CNAME` record for `www` to **DNS only (grey cloud)**.
416
-
417
- 2. **Trigger Renewal on Droplet:**
418
- * Wait 1-2 minutes for the DNS change to propagate.
419
- * SSH into your droplet and restart Caddy. This will trigger it to check its certificates and renew if necessary.
420
- ```bash
421
- ssh root@{{DROPLET_TAILSCALE_IP}} "sudo systemctl restart caddy"
422
- ```
423
-
424
- 3. **Verify Renewal:**
425
- * Watch the Caddy logs for a success message.
426
- ```bash
427
- # Using just:
428
- just deploy-caddy-logs
429
- # Or manually:
430
- ssh root@{{DROPLET_TAILSCALE_IP}} "sudo journalctl -u caddy -f"
431
- ```
432
-
433
- 4. **Re-enable Cloudflare Proxy:**
434
- * Once renewal is confirmed, go back to your Cloudflare DNS settings.
435
- * Change the `A` and `CNAME` records back to **Proxied (orange cloud)**.
436
-
437
- ### Monitoring the System
438
-
439
- #### DNS, Proxy, and Caddy Health
440
- - **Check DNS Resolution**: Verify that your domain points to Cloudflare's IPs.
441
- ```bash
442
- dig nicholasgrundl.com
443
- ```
444
- - **Check Public Accessibility**: Ensure the site is returning a `200 OK` status through Cloudflare.
445
- ```bash
446
- curl -I https://nicholasgrundl.com
447
- ```
448
- - **Check Caddy Service Status**: Make sure the Caddy service is active and running on the droplet.
449
- ```bash
450
- ssh root@{{DROPLET_TAILSCALE_IP}} "sudo systemctl status caddy"
451
- ```
452
-
453
- #### Application & Docker Health
454
- - **Check Container Status**: Verify that your application's Docker container is running.
455
- ```bash
456
- # Using just:
457
- just deploy-status
458
- # Or manually:
459
- ssh root@{{DROPLET_TAILSCALE_IP}} "docker compose -f /opt/personal-website/docker-compose.yml ps"
460
- ```
461
- - **Check Application Logs**: View the logs from your application container for any errors.
462
- ```bash
463
- # Using just:
464
- just deploy-logs-follow
465
- # Or manually:
466
- ssh root@{{DROPLET_TAILSCALE_IP}} "docker compose -f /opt/personal-website/docker-compose.yml logs -f"
467
- ```
468
-
469
- #### Traffic and Analytics
470
- - **Caddy Request Logs**: Caddy logs all requests by default. You can view them via `journalctl` to see incoming traffic to your origin server.
471
- ```bash
472
- ssh root@{{DROPLET_TAILSCALE_IP}} "sudo journalctl -u caddy"
473
- ```
474
- - **Cloudflare Analytics**: The Cloudflare dashboard provides a powerful, free analytics suite. Go to the **Analytics & Logs** tab for your domain to see detailed information about traffic, requests, cached content, security events, and performance. This is the best place to get an overview of your site's traffic.
475
-
476
- ---
477
-
478
- ## Part 6: Future Improvements
479
-
480
- This section contains optional but highly recommended enhancements to make your deployment more robust, secure, and easier to manage.
481
-
482
- ### Automate SSL Certificate Renewal
483
- As described in the maintenance section, the default Caddy setup requires manual intervention to renew SSL certificates. You can eliminate this by using a free Cloudflare Origin Certificate, which is valid for 15 years.
484
-
485
- **Workflow Summary:**
486
- 1. **Generate Certificate in Cloudflare:** Go to **SSL/TLS** -> **Origin Server** in your Cloudflare dashboard and create a 15-year certificate.
487
- 2. **Copy Certificate to Droplet:** Save the certificate and private key, and copy them to `/etc/caddy/certs/` on your droplet.
488
- 3. **Update `Caddyfile`:** Modify your `Caddyfile` to use these files with the `tls` directive:
489
- ```caddy
490
- tls /etc/caddy/certs/nicholasgrundl.com.pem /etc/caddy/certs/nicholasgrundl.com.key
491
- ```
492
- 4. **Deploy and Reload:** Deploy the new `Caddyfile` and reload Caddy.
493
-
494
- > For full step-by-step instructions, see the `design/cloudflare-origin-cert.md` file.
495
-
496
- ### Security Hardening
497
-
498
- #### Step A: Lock SSH to Tailscale Only
499
-
500
- **⚠️ CRITICAL**: Verify Tailscale access works before this step!
501
-
502
- ```bash
503
- # Test Tailscale SSH
504
- ssh root@{{DROPLET_TAILSCALE_IP}}
505
- ```
506
-
507
- **Update Digital Ocean Firewall:**
508
-
509
- 1. Digital Ocean dashboard → **Networking** → **Firewalls**
510
- 2. Edit firewall: `http-https`
511
- 3. **Inbound Rules** → SSH (port 22):
512
- - Remove "All IPv4" and "All IPv6".
513
- - Add a new source: `100.0.0.0/8` (Tailscale's IP range).
514
- 4. Save firewall and test that public IP SSH access is blocked while Tailscale access works.
515
-
516
- #### Step B: Configure UFW Firewall
517
-
518
- ```bash
519
- ssh root@{{DROPLET_TAILSCALE_IP}}
520
- sudo ufw allow from 100.0.0.0/8 to any port 22 proto tcp
521
- sudo ufw allow 41641/udp
522
- sudo ufw allow 80/tcp
523
- sudo ufw allow 443/tcp
524
- sudo ufw default deny incoming
525
- sudo ufw default allow outgoing
526
- sudo ufw enable
527
- ```
528
-
529
- #### Step C: Install fail2ban
530
- ```bash
531
- ssh root@{{DROPLET_TAILSCALE_IP}} "sudo apt install -y fail2ban && sudo systemctl enable --now fail2ban"
532
- ```
533
-
534
- #### Step D: Enable Automatic Security Updates
535
- ```bash
536
- ssh root@{{DROPLET_TAILSCALE_IP}} "sudo apt install -y unattended-upgrades && sudo dpkg-reconfigure -plow unattended-upgrades"
537
- ```
538
-
539
- ### Deployment Automation
540
-
541
- This project uses [Just](https://github.com/casey/just) for deployment automation. The `justfile` in the project root contains all the necessary commands.
542
-
543
- **View all available commands:**
544
- ```bash
545
- just --list
546
- ```
547
-
548
- **Deploy to production:**
549
- ```bash
550
- just deploy
551
- ```
552
-
553
- ### Automated Backups & Log Rotation
554
-
555
- #### Step A: Set Up Backup Script (On Droplet)
556
- A sample backup script and cron job are detailed in the appendix. This should be reviewed and implemented to ensure regular backups of your configuration and data.
557
-
558
- #### Step B: Configure Log Rotation
559
- To prevent logs from consuming excessive disk space, configure Docker's log rotation.
560
-
561
- **Add to `/etc/docker/daemon.json` on the droplet:**
562
- ```json
563
- {
564
- "log-driver": "json-file",
565
- "log-opts": {
566
- "max-size": "10m",
567
- "max-file": "3"
568
- }
569
- }
570
- ```
571
- Then restart Docker: `sudo systemctl restart docker`.
572
-
573
- ---
574
-
575
- ## Troubleshooting
576
-
577
- **Site not loading?**
578
- - Check DNS: `dig nicholasgrundl.com`
579
- - Check container: `just deploy-status`
580
- - Check Caddy: `ssh root@{{DROPLET_TAILSCALE_IP}} "sudo systemctl status caddy"`
581
-
582
- **SSL not working?**
583
- - Check Caddy logs: `just deploy-caddy-logs`
584
- - Verify DNS propagation: https://dnschecker.org
585
-
586
- **Can't SSH?**
587
- - Use Digital Ocean web console (dashboard → droplet → Access → Launch Console)
588
- - Check Tailscale: `tailscale status`
589
-
590
- ---
591
-
592
- ## Appendix: Migrating to Docker Buildx
593
-
594
- (This section remains unchanged)
595
-
@@ -1,46 +0,0 @@
1
- # Next Steps
2
-
3
- Overview of remaining work before the first public release. Each item has a dedicated planning document with full details.
4
-
5
- ## Decisions Made
6
-
7
- - **Audience:** End users at various client organizations, each with their own IAP-protected API. All have Gmail or Google Workspace accounts.
8
- - **Distribution:** Public PyPI (`pip install tokentoss`)
9
- - **CI/CD:** GitHub Actions with tag-triggered releases via PyPI trusted publishers (OIDC)
10
-
11
- ---
12
-
13
- ## Plan Documents
14
-
15
- ### 02 — Architectural Analysis
16
- Observations on current limitations (detached token refresh, etc.) and potential improvements for robustness and scalability.
17
-
18
- ### 03 — Widgets Subpackage
19
- Refactor `widget.py` and `configure_widget.py` into a `widgets/` subpackage. Detailed migration steps, import changes, and an optional future split for `CallbackServer`.
20
-
21
- ### 04 — Distribution (PyPI)
22
- Public PyPI setup: `pyproject.toml` metadata, README updates, open-source scaffolding (LICENSE, CONTRIBUTING.md, issue templates), and manual PyPI account/trusted publisher setup steps.
23
-
24
- ### 05 — CI/CD (GitHub Actions)
25
- Two workflows: `ci.yml` (lint, typecheck, test matrix on 3.10/3.11/3.12) and `release.yml` (tag-triggered build + PyPI publish + GitHub Release). Full YAML included.
26
-
27
- ### 06 — Test Service (Cloud Run + IAP)
28
- A FastAPI microservice deployed behind IAP on Cloud Run to verify tokens end-to-end and demonstrate user-specific content. Full GCP setup walkthrough from zero. Lives at `examples/test-service/`.
29
-
30
- ### 07 — Documentation & Tutorials
31
- Three-tier doc strategy: Tier 1 (quick-start guide + GCP admin setup for v0.1.0), Tier 2 (API reference + example notebooks), Tier 3 (hosted docs site, troubleshooting guide).
32
-
33
- ### 08 — Manual Testing
34
- Pre-release checklist covering ConfigureWidget, GoogleAuthWidget, IAPClient, token refresh, sign out/re-auth, and edge cases. Run in JupyterLab before any tagged release.
35
-
36
- ---
37
-
38
- ## Suggested Execution Order
39
-
40
- 1. **Widgets refactor** (03) — code cleanup before release
41
- 2. **Distribution prep** (04) — LICENSE, CONTRIBUTING, pyproject.toml URLs
42
- 3. **CI/CD setup** (05) — get workflows running on PRs
43
- 4. **Test service** (06) — deploy verification endpoint
44
- 5. **Manual testing** (08) — verify full flow end-to-end
45
- 6. **Docs** (07) — at minimum Tier 1 before tagging v0.1.0
46
- 7. **First release** — `git tag v0.1.0 && git push origin v0.1.0`
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes