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.
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/workflows/release.yml +1 -1
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/workflows/start-release.yml +8 -2
- {tokentoss-0.1.0 → tokentoss-0.1.1}/PKG-INFO +1 -1
- tokentoss-0.1.1/_blueprint/plan/01-next-steps.md +38 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/pyproject.toml +1 -1
- tokentoss-0.1.0/_blueprint/ideas/nicholasgrundl-site-deployment.md +0 -595
- tokentoss-0.1.0/_blueprint/plan/01-next-steps.md +0 -46
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/agents/docs-updater.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/commit-auto.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/commit-macro.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/commit-manual.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/design-plan.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/design-review.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/tasks-align.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/tasks-implement.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/work-archive.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/commands/work-status.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/hooks/commit-auto.sh +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/hooks/macro-commit.sh +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/hooks/micro-commit.sh +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.claude/hooks/templates/macro-commit-prompt.txt +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/commands/commit/auto.toml +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/commands/commit/macro.toml +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/commands/commit/manual.toml +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/hooks/commit-auto.sh +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/hooks/macro-commit.sh +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/hooks/micro-commit.sh +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/hooks/templates/macro-commit-prompt.txt +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/settings.json +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gemini/skills/docs-updater/SKILL.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/workflows/ci.yml +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.github/workflows/security.yml +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/.gitignore +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/CLAUDE.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/CONTRIBUTING.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/LICENSE +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/README.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/CLAUDE.md +0 -0
- {tokentoss-0.1.0/_blueprint/plan → tokentoss-0.1.1/_blueprint/archive}/04-distribution-pypi.md +0 -0
- {tokentoss-0.1.0/_blueprint/plan → tokentoss-0.1.1/_blueprint/archive}/05-ci-cd-github-actions.md +0 -0
- {tokentoss-0.1.0/_blueprint/plan → tokentoss-0.1.1/_blueprint/archive}/08-manual-testing.md +0 -0
- {tokentoss-0.1.0/_blueprint/implement → tokentoss-0.1.1/_blueprint/archive}/A-github-actions-setup.md +0 -0
- {tokentoss-0.1.0/_blueprint/implement → tokentoss-0.1.1/_blueprint/archive}/B-initial-release-plan.md +0 -0
- {tokentoss-0.1.0/_blueprint/implement → tokentoss-0.1.1/_blueprint/archive}/RELEASE-1-merge-and-publish.md +0 -0
- {tokentoss-0.1.0/_blueprint/implement → tokentoss-0.1.1/_blueprint/archive}/RELEASE-2-v010-pypi.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/bugfix-implementation.md +0 -0
- {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/ci-cd-notes.md +0 -0
- {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/cleanup-notes.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/client_secrets_implementation.md +0 -0
- {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-concept.md +0 -0
- {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-outline-claude.md +0 -0
- {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-outline-codex.md +0 -0
- {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-outline-gemini.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/phase-2-implementation.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/phase-3-implementation.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/phase-5-polish-implementation.md +0 -0
- {tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/plan-v1.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/polish-improvement-implementation.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/progress.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/testing-improvement-implementation.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/tokentoss_implementation.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/widget-testing-feedback.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/README.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/agent-skills.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/custom-commands.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/headless-mode.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/hooks-reference.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/hooks.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/sandboxing.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/settings.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/sub-agents.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/gemini-cli/system-prompt-override.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/google-iap/README.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/platformdirs/README.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/platformdirs/directory-types.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/platformdirs/platformdirs-overview.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest/pytest-general-foundations.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest/pytest-project-guidance.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest-mock/configuration.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest-mock/overview.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest-mock/remarks.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/pytest-mock/usage.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ruff/README.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/01-overview.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/02-github-readme.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/03-configuration.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/04-cli-reference.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/05-editors.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/06-configuration-reference.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/07-environment-variables.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/ty/README.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/implement/C-release-implementation.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/implement/D-test-service-gcp-setup.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/implement/E-widget-testing-guide.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/plan/02-architectural-analysis.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/plan/03-widgets-subpackage.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/plan/06-test-service-cloud-run.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/plan/07-docs-and-tutorials.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/docs/gcp-admin-setup.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/docs/quickstart.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/Dev-prerelease-testing.ipynb +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/Widget-Testing.ipynb +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/test-service/Dockerfile +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/test-service/README.md +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/test-service/main.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/examples/test-service/requirements.txt +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/justfile +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/__init__.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/_logging.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/_telemetry.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/auth_manager.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/client.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/configure_widget.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/exceptions.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/setup.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/storage.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/src/tokentoss/widget.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/__init__.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_auth_manager.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_client.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_configure_widget.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_setup.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_storage.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/tests/test_widget.py +0 -0
- {tokentoss-0.1.0 → tokentoss-0.1.1}/uv.lock +0 -0
|
@@ -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.
|
|
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,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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tokentoss-0.1.0/_blueprint/plan → tokentoss-0.1.1/_blueprint/archive}/04-distribution-pypi.md
RENAMED
|
File without changes
|
{tokentoss-0.1.0/_blueprint/plan → tokentoss-0.1.1/_blueprint/archive}/05-ci-cd-github-actions.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tokentoss-0.1.0/_blueprint/implement → tokentoss-0.1.1/_blueprint/archive}/RELEASE-2-v010-pypi.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-outline-claude.md
RENAMED
|
File without changes
|
{tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-outline-codex.md
RENAMED
|
File without changes
|
{tokentoss-0.1.0/_blueprint/ideas → tokentoss-0.1.1/_blueprint/archive}/design-outline-gemini.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/archive/testing-improvement-implementation.md
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tokentoss-0.1.0 → tokentoss-0.1.1}/_blueprint/context/platformdirs/platformdirs-overview.md
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|