k8s-portal: document all three cluster-access paths in onboarding

The Getting Started portal only walked through the heaviest path (local VPN + kubectl + Vault + sops install) and never mentioned the two zero-setup routes that users actually reach first. Restructure onboarding to lead with all three, recommendation first: (A) the t3 web terminal, which drops you into a ready shell with kubectl/Vault/repos preinstalled; (B) the k8s web dashboard, auto-authenticated per user; and (C) the existing own-machine setup. Flag the dashboard/terminal as the fallback when CLI OIDC login is unavailable, reframe the misleading home-page 'VPN required' banner (only path C needs it), add the access endpoints to the service catalog, and fix a stale Vaultwarden URL (was vault.viktorbarzin.me, which is actually HashiCorp Vault; Vaultwarden is vaultwarden.viktorbarzin.me).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-06-27 16:34:36 +00:00
parent d4f564e8d5
commit fca948a23d
4 changed files with 330 additions and 201 deletions

View file

@ -5,9 +5,11 @@
<main>
<h1>Kubernetes Access Portal</h1>
<div class="callout warning">
<strong>VPN Required</strong> — The cluster is on a private network. You need Headscale VPN access before kubectl will work.
<a href="/onboarding">See the Getting Started guide</a> for VPN setup instructions.
<div class="callout info">
<strong>Fastest way in:</strong> open the <a href="https://t3.viktorbarzin.me">web terminal</a> or the
<a href="https://k8s.viktorbarzin.me">dashboard</a> and sign in — no install, no VPN needed. Prefer your
own machine? The <a href="/onboarding#path-laptop">local-setup guide</a> covers VPN + kubectl, and the
<a href="/onboarding">Getting Started page</a> compares all three access paths.
</div>
<section>
@ -26,6 +28,7 @@
<p><strong>Assigned namespaces:</strong> {data.namespaces.join(', ')}</p>
<h3>Quick Commands</h3>
<p>Run these as-is in the <a href="https://t3.viktorbarzin.me">web terminal</a> — it's already signed in as you.</p>
<pre>
# Check your pods
kubectl get pods -n {data.namespaces[0]}
@ -47,16 +50,23 @@ vault write kubernetes/creds/{data.namespaces[0]}-deployer \
<section>
<h2>Get Started</h2>
<h3>No setup — start now</h3>
<ol>
<li><a href="https://t3.viktorbarzin.me">Open the web terminal</a> — a ready shell with kubectl, Vault and your repos already set up</li>
<li><a href="https://k8s.viktorbarzin.me">Open the dashboard</a> — point-and-click view of your workloads</li>
</ol>
<h3>On your own machine</h3>
<ol>
{#if data.role === 'namespace-owner'}
<li><a href="/onboarding?role=namespace-owner">Complete the namespace-owner onboarding guide</a></li>
<li><a href="/onboarding?role=namespace-owner#path-laptop">Follow the namespace-owner setup</a> (VPN, kubectl, Vault, encrypted state)</li>
{:else}
<li><a href="/onboarding">Complete the onboarding guide</a> (VPN, kubectl, git)</li>
<li><a href="/onboarding#path-laptop">Follow the local setup</a> (VPN, kubectl, git)</li>
{/if}
<li><a href="/setup">Install kubectl and kubelogin</a></li>
<li><a href="/download">Download your kubeconfig</a></li>
<li>Run <code>kubectl get namespaces</code> to verify access</li>
</ol>
<p><a href="/onboarding">Compare all three access paths →</a></p>
</section>
<section>
@ -91,12 +101,12 @@ vault write kubernetes/creds/{data.namespaces[0]}-deployer \
border-radius: 6px;
margin: 1rem 0;
}
.callout.warning {
background: #fff3cd;
border-left: 4px solid #ffc107;
.callout.info {
background: #e8f4fd;
border-left: 4px solid #2196f3;
}
.callout a {
color: #856404;
color: #0d47a1;
font-weight: 600;
}
</style>

View file

@ -5,87 +5,175 @@
<main class="content">
<h1>Getting Started</h1>
<p>Welcome! Follow these steps to get access to the home Kubernetes cluster.</p>
<div class="role-tabs">
<a href="/onboarding" class:active={!showNamespaceOwner}>General User</a>
<a href="/onboarding?role=namespace-owner" class:active={showNamespaceOwner}>Namespace Owner</a>
</div>
<p>
Welcome! There are three ways to reach the home Kubernetes cluster. Pick the one that fits —
the first two need <strong>zero setup</strong> and open right in your browser.
</p>
<section>
<h2>Step 0 — Join the VPN</h2>
<p>The cluster is on a private network (<code>10.0.20.0/24</code>). You need VPN access first.</p>
<h2>Three ways in</h2>
<table>
<thead><tr><th>Path</th><th>Best for</th><th>Setup</th></tr></thead>
<tbody>
<tr>
<td><a href="#path-terminal"><strong>A — Web terminal</strong></a></td>
<td>Just want to start working now</td>
<td>None — opens in your browser</td>
</tr>
<tr>
<td><a href="#path-dashboard"><strong>B — Web dashboard</strong></a></td>
<td>Click around, watch your app, read logs</td>
<td>None — opens in your browser</td>
</tr>
<tr>
<td><a href="#path-laptop"><strong>C — Your own machine</strong></a></td>
<td>kubectl / Terraform locally, full control</td>
<td>VPN + one-line installer</td>
</tr>
</tbody>
</table>
<div class="callout info">
<strong>Not sure?</strong> Start with the <a href="#path-terminal">web terminal (Path A)</a>.
Everything is already installed and your repos are already cloned — you can run your first
<code>kubectl</code> command within a minute, from any device.
</div>
</section>
<section id="path-terminal" class="path">
<h2>Path A — Web terminal <span class="badge rec">Recommended</span> <span class="badge none">No setup</span></h2>
<p>
A full terminal that runs in your browser — nothing to install, works from any device
(even a tablet). It drops you into your own account on the shared workstation, with every
tool already set up.
</p>
<ol>
<li>Install <a href="https://tailscale.com/download" target="_blank">Tailscale</a> for your OS</li>
<li>Run this in your terminal:
<pre>tailscale login --login-server https://headscale.viktorbarzin.me</pre>
<li>Open <a href="https://t3.viktorbarzin.me" target="_blank">t3.viktorbarzin.me</a></li>
<li>Sign in with your Authentik account (the same SSO login as this portal)</li>
<li>You land in a ready-to-use shell. Try it:
<pre>kubectl get pods -n YOUR_NAMESPACE</pre>
</li>
<li>A browser window will open with a registration URL</li>
<li>Send that URL to Viktor via email (<a href="mailto:vbarzin@gmail.com">vbarzin@gmail.com</a>) or Slack</li>
<li>Wait for approval (usually within a few hours)</li>
<li>Once approved, test: <pre>ping 10.0.20.100</pre></li>
</ol>
<div class="callout info">
<strong>Already done for you</strong> on the workstation:
<ul>
<li><code>kubectl</code> + your kubeconfig, scoped to your namespaces (no login dance)</li>
<li><code>vault</code>, <code>terragrunt</code>, <code>terraform</code>, <code>sops</code>, <code>kubeseal</code></li>
<li>Your repos cloned under <code>~/code</code> — the <code>infra</code> repo plus your own project repos</li>
<li>Claude Code, ready to pair with you on changes</li>
</ul>
</div>
<div class="callout warning">
<strong>No access yet?</strong> The workstation is provisioned per person. If
<code>t3.viktorbarzin.me</code> says you're not authorized, ask Viktor to add you
(<a href="mailto:vbarzin@gmail.com">vbarzin@gmail.com</a> or Slack).
</div>
</section>
<section>
<h2>Step 1 — Log in to the portal</h2>
<p>Visit <a href="https://k8s-portal.viktorbarzin.me">k8s-portal.viktorbarzin.me</a> and sign in with your Authentik account.</p>
<p>If you don't have an account yet, ask Viktor to create one.</p>
<section id="path-dashboard" class="path">
<h2>Path B — Web dashboard <span class="badge none">No setup</span></h2>
<p>
A point-and-click view of the cluster — browse your pods, read logs, restart a deployment,
check events. Nothing to install.
</p>
<ol>
<li>Open <a href="https://k8s.viktorbarzin.me" target="_blank">k8s.viktorbarzin.me</a></li>
<li>Sign in with your Authentik account</li>
<li>
You're dropped straight into the Kubernetes Dashboard, already authenticated as you —
<strong>no token to paste</strong>. The portal injects your personal access token for you.
</li>
</ol>
<div class="callout info">
Scoped to your namespace(s): you can see and manage your own workloads, but not other
tenants'. This path uses a per-user token that does <em>not</em> depend on CLI login, so it
keeps working even if <code>kubectl</code> OIDC login is having a bad day — making it the
reliable fallback for Path C.
</div>
</section>
<section>
<h2>Step 2 — Set up kubectl</h2>
<p>Run one of these commands in your terminal to install everything automatically:</p>
<h3>macOS</h3>
<p class="prereq">Requires <a href="https://brew.sh" target="_blank">Homebrew</a>. Install it first if you don't have it.</p>
<pre>bash &lt;(curl -fsSL https://k8s-portal.viktorbarzin.me/setup/script?os=mac)</pre>
<h3>Linux</h3>
<pre>bash &lt;(curl -fsSL https://k8s-portal.viktorbarzin.me/setup/script?os=linux)</pre>
<h3>Windows</h3>
<p>Use <a href="https://learn.microsoft.com/en-us/windows/wsl/install" target="_blank">WSL2</a> and follow the Linux instructions.</p>
</section>
<section id="path-laptop" class="path c">
<h2>Path C — From your own machine</h2>
<p>
For running <code>kubectl</code>, <code>vault</code> and Terraform locally. This is the most
powerful path and the one to use for infrastructure changes — it just needs a bit more setup
because the cluster API lives on a private network.
</p>
<div class="role-tabs">
<a href="/onboarding?role=general#path-laptop" class:active={!showNamespaceOwner}>General User</a>
<a href="/onboarding?role=namespace-owner#path-laptop" class:active={showNamespaceOwner}>Namespace Owner</a>
</div>
<p class="prereq">
{#if showNamespaceOwner}
Namespace owner — you'll also set up Vault and encrypted Terraform state so you can deploy
your own app stacks.
{:else}
General user — VPN, kubectl and git access. (Managing your own app stack? Switch to the
<strong>Namespace Owner</strong> tab above.)
{/if}
</p>
{#if showNamespaceOwner}
<section>
<h2>Step 3 — Log into Vault</h2>
<p>Vault manages your secrets and issues dynamic Kubernetes credentials.</p>
<pre>vault login -method=oidc</pre>
<p>This opens your browser for Authentik SSO. After login, your token is saved to <code>~/.vault-token</code>.</p>
<h3>Step 1 — Join the VPN</h3>
<p>The cluster API is on a private network (<code>10.0.20.0/24</code>), so you need VPN access first.</p>
<ol>
<li>Install <a href="https://tailscale.com/download" target="_blank">Tailscale</a> for your OS</li>
<li>Run this in your terminal:
<pre>tailscale login --login-server https://headscale.viktorbarzin.me</pre>
</li>
<li>A browser window opens with a registration URL</li>
<li>Send that URL to Viktor via email (<a href="mailto:vbarzin@gmail.com">vbarzin@gmail.com</a>) or Slack</li>
<li>Wait for approval (usually within a few hours)</li>
<li>Once approved, test: <pre>ping 10.0.20.100</pre></li>
</ol>
</section>
<section>
<h2>Step 4 — Verify kubectl access</h2>
<p>Run this command. It will open your browser for OIDC login the first time:</p>
<pre>kubectl get pods -n YOUR_NAMESPACE</pre>
<p>You should see an empty list (no resources) or your running pods.</p>
<h3>Step 2 — Install the tools</h3>
<p>Run one of these to install everything automatically (kubectl, kubelogin, vault, terragrunt, terraform, kubeseal) and write your kubeconfig to <code>~/.kube/config-home</code>:</p>
<h4>macOS</h4>
<p class="prereq">Requires <a href="https://brew.sh" target="_blank">Homebrew</a>. Install it first if you don't have it.</p>
<pre>bash &lt;(curl -fsSL https://k8s-portal.viktorbarzin.me/setup/script?os=mac)</pre>
<h4>Linux</h4>
<pre>bash &lt;(curl -fsSL https://k8s-portal.viktorbarzin.me/setup/script?os=linux)</pre>
<h4>Windows</h4>
<p>Use <a href="https://learn.microsoft.com/en-us/windows/wsl/install" target="_blank">WSL2</a> and follow the Linux instructions.</p>
</section>
<section>
<h2>Step 5 — Clone the infra repo</h2>
<pre>git clone https://github.com/ViktorBarzin/infra.git
<h3>Step 3 — Verify access</h3>
<p>Run this. The first time, it opens your browser for SSO login:</p>
<pre>kubectl get {showNamespaceOwner ? 'pods -n YOUR_NAMESPACE' : 'namespaces'}</pre>
<p>You should see your resources (or an empty list if you haven't deployed anything yet).</p>
<div class="callout warning">
<strong>Browser login loops, or kubectl says "Unauthorized"?</strong> Command-line SSO
(OIDC) can occasionally be unavailable. When that happens, use the
<a href="#path-dashboard">web dashboard (Path B)</a> or the
<a href="#path-terminal">web terminal (Path A)</a> — both authenticate a different way and
keep working — and let Viktor know.
</div>
<p class="prereq">Connection error instead? Make sure the VPN is up: <code>tailscale status</code>.</p>
</section>
{#if showNamespaceOwner}
<section>
<h3>Step 4 — Log into Vault</h3>
<p>Vault manages your secrets and issues dynamic Kubernetes credentials.</p>
<pre>vault login -method=oidc</pre>
<p>This opens your browser for Authentik SSO. After login, your token is saved to <code>~/.vault-token</code>.</p>
</section>
<section>
<h3>Step 5 — Clone the infra repo</h3>
<pre>git clone https://github.com/ViktorBarzin/infra.git
cd infra</pre>
<p>This is where all the infrastructure configuration lives. Terraform state is committed as encrypted files.</p>
</section>
<p>This is where all the infrastructure configuration lives. Terraform state is committed as encrypted files.</p>
</section>
<section>
<h2>Step 6 — Install tools</h2>
<p>You need <code>sops</code> and <code>terragrunt</code> to work with infrastructure state:</p>
<h3>macOS</h3>
<pre>brew install sops terragrunt</pre>
<h3>Linux</h3>
<pre># sops
curl -LO https://github.com/getsops/sops/releases/latest/download/sops-v3.9.4.linux.amd64
sudo mv sops-*.linux.amd64 /usr/local/bin/sops && sudo chmod +x /usr/local/bin/sops
# terragrunt
curl -LO https://github.com/gruntwork-io/terragrunt/releases/latest/download/terragrunt_linux_amd64
sudo mv terragrunt_linux_amd64 /usr/local/bin/terragrunt && sudo chmod +x /usr/local/bin/terragrunt</pre>
</section>
<section>
<h2>Step 7 — Decrypt your state</h2>
<p>Terraform state is encrypted with SOPS. Your Vault login gives you access to <strong>only your stacks</strong>.</p>
<pre># Make sure you're logged into Vault
<section>
<h3>Step 6 — Decrypt your state</h3>
<p>Terraform state is encrypted with SOPS. Your Vault login gives you access to <strong>only your stacks</strong>.</p>
<pre># Make sure you're logged into Vault
vault login -method=oidc
# Decrypt your stack's state
@ -95,160 +183,157 @@ scripts/state-sync decrypt YOUR_NAMESPACE
cd stacks/YOUR_NAMESPACE
../../scripts/tg plan</pre>
<div class="diagram">
<h3>How state encryption works</h3>
<div class="flow-diagram">
<div class="flow-row">
<div class="flow-box accent">vault login -method=oidc</div>
<div class="flow-arrow"></div>
<div class="flow-box">Authentik SSO</div>
<div class="flow-arrow"></div>
<div class="flow-box accent">~/.vault-token</div>
</div>
<div class="flow-separator"></div>
<div class="flow-row">
<div class="flow-box accent">scripts/tg plan</div>
<div class="flow-arrow"></div>
<div class="flow-box">state-sync decrypt</div>
<div class="flow-arrow"></div>
<div class="flow-box">Vault Transit<br/><small>sops-state-YOUR_NS</small></div>
</div>
<div class="flow-separator"></div>
<div class="flow-row">
<div class="flow-box">terragrunt plan/apply</div>
<div class="flow-arrow"></div>
<div class="flow-box">state-sync encrypt</div>
<div class="flow-arrow"></div>
<div class="flow-box accent">git commit + push</div>
<div class="diagram">
<h3>How state encryption works</h3>
<div class="flow-diagram">
<div class="flow-row">
<div class="flow-box accent">vault login -method=oidc</div>
<div class="flow-arrow"></div>
<div class="flow-box">Authentik SSO</div>
<div class="flow-arrow"></div>
<div class="flow-box accent">~/.vault-token</div>
</div>
<div class="flow-separator"></div>
<div class="flow-row">
<div class="flow-box accent">scripts/tg plan</div>
<div class="flow-arrow"></div>
<div class="flow-box">state-sync decrypt</div>
<div class="flow-arrow"></div>
<div class="flow-box">Vault Transit<br/><small>sops-state-YOUR_NS</small></div>
</div>
<div class="flow-separator"></div>
<div class="flow-row">
<div class="flow-box">terragrunt plan/apply</div>
<div class="flow-arrow"></div>
<div class="flow-box">state-sync encrypt</div>
<div class="flow-arrow"></div>
<div class="flow-box accent">git commit + push</div>
</div>
</div>
</div>
</div>
<div class="callout info">
<strong>Access control:</strong> You can only decrypt state for your own namespaces.
Each namespace has its own Vault Transit encryption key. Your Vault policy
(<code>sops-user-YOUR_USERNAME</code>) only grants access to your keys.
</div>
</section>
<div class="callout info">
<strong>Access control:</strong> You can only decrypt state for your own namespaces.
Each namespace has its own Vault Transit encryption key. Your Vault policy
(<code>sops-user-YOUR_USERNAME</code>) only grants access to your keys.
</div>
</section>
<section>
<h2>Step 8 — Create your first app stack</h2>
<ol>
<li>Copy the template: <pre>cp -r stacks/_template stacks/myapp
<section>
<h3>Step 7 — Create your first app stack</h3>
<ol>
<li>Copy the template: <pre>cp -r stacks/_template stacks/myapp
mv stacks/myapp/main.tf.example stacks/myapp/main.tf</pre></li>
<li>Edit <code>stacks/myapp/main.tf</code> — replace all <code>&lt;placeholders&gt;</code></li>
<li>Store secrets in Vault:
<pre>vault kv put secret/YOUR_USERNAME/myapp DB_PASSWORD=secret123</pre>
</li>
<li>Apply your stack:
<pre>cd stacks/myapp && ../../scripts/tg apply</pre>
</li>
<li>Commit encrypted state:
<pre>cd ../..
<li>Edit <code>stacks/myapp/main.tf</code> — replace all <code>&lt;placeholders&gt;</code></li>
<li>Store secrets in Vault:
<pre>vault kv put secret/YOUR_USERNAME/myapp DB_PASSWORD=secret123</pre>
</li>
<li>Apply your stack:
<pre>cd stacks/myapp && ../../scripts/tg apply</pre>
</li>
<li>Commit encrypted state:
<pre>cd ../..
git add stacks/myapp/ state/stacks/myapp/terraform.tfstate.enc
git commit -m "add myapp stack"
git push</pre>
</li>
</ol>
</section>
</li>
</ol>
</section>
<section>
<h2>Architecture Overview</h2>
<p>Here's how your changes flow through the system:</p>
<section>
<h3>Architecture Overview</h3>
<p>Here's how your changes flow through the system:</p>
<div class="diagram">
<h3>Apply workflow</h3>
<div class="arch-grid">
<div class="arch-col">
<div class="arch-header">Your Machine</div>
<div class="arch-box">git pull</div>
<div class="arch-arrow"></div>
<div class="arch-box">scripts/tg plan</div>
<div class="arch-arrow"><small>auto-decrypt</small></div>
<div class="arch-box">scripts/tg apply</div>
<div class="arch-arrow"><small>auto-encrypt</small></div>
<div class="arch-box">git push</div>
</div>
<div class="arch-col">
<div class="arch-header">Vault</div>
<div class="arch-box small">OIDC auth<br/><small>Authentik SSO</small></div>
<div class="arch-arrow"></div>
<div class="arch-box small">Transit decrypt<br/><small>sops-state-*</small></div>
<div class="arch-arrow"></div>
<div class="arch-box small">Transit encrypt<br/><small>per-stack key</small></div>
</div>
<div class="arch-col">
<div class="arch-header">Cluster</div>
<div class="arch-box small">K8s API</div>
<div class="arch-arrow"></div>
<div class="arch-box small">Your namespace<br/><small>pods, services</small></div>
<div class="arch-arrow"></div>
<div class="arch-box small">Traefik ingress<br/><small>*.viktorbarzin.me</small></div>
<div class="diagram">
<h3>Apply workflow</h3>
<div class="arch-grid">
<div class="arch-col">
<div class="arch-header">Your Machine</div>
<div class="arch-box">git pull</div>
<div class="arch-arrow"></div>
<div class="arch-box">scripts/tg plan</div>
<div class="arch-arrow"><small>auto-decrypt</small></div>
<div class="arch-box">scripts/tg apply</div>
<div class="arch-arrow"><small>auto-encrypt</small></div>
<div class="arch-box">git push</div>
</div>
<div class="arch-col">
<div class="arch-header">Vault</div>
<div class="arch-box small">OIDC auth<br/><small>Authentik SSO</small></div>
<div class="arch-arrow"></div>
<div class="arch-box small">Transit decrypt<br/><small>sops-state-*</small></div>
<div class="arch-arrow"></div>
<div class="arch-box small">Transit encrypt<br/><small>per-stack key</small></div>
</div>
<div class="arch-col">
<div class="arch-header">Cluster</div>
<div class="arch-box small">K8s API</div>
<div class="arch-arrow"></div>
<div class="arch-box small">Your namespace<br/><small>pods, services</small></div>
<div class="arch-arrow"></div>
<div class="arch-box small">Traefik ingress<br/><small>*.viktorbarzin.me</small></div>
</div>
</div>
</div>
</div>
<div class="diagram">
<h3>Security model</h3>
<table>
<thead><tr><th>Layer</th><th>What</th><th>How</th></tr></thead>
<tbody>
<tr><td>Authentication</td><td>Who are you?</td><td>Authentik SSO (OIDC) → Vault token</td></tr>
<tr><td>Authorization</td><td>What can you access?</td><td>Vault policy (<code>sops-user-*</code>) scoped to your namespaces</td></tr>
<tr><td>Encryption at rest</td><td>State in git</td><td>SOPS + Vault Transit (per-stack key)</td></tr>
<tr><td>Encryption fallback</td><td>Bootstrap / DR</td><td>age keys (admin only)</td></tr>
<tr><td>Network</td><td>Cluster access</td><td>Headscale VPN (private 10.0.20.0/24)</td></tr>
</tbody>
</table>
</div>
</section>
{:else}
<section>
<h2>Step 3 — Verify access</h2>
<p>Run this command. It will open your browser for login the first time:</p>
<pre>kubectl get namespaces</pre>
<p>You should see output like:</p>
<pre class="output">NAME STATUS AGE
default Active 200d
kube-system Active 200d
monitoring Active 200d
...</pre>
<p>If you get a connection error, make sure your VPN is connected (<code>tailscale status</code>).</p>
</section>
<section>
<h2>Step 4 — Clone the repo</h2>
<pre>git clone https://github.com/ViktorBarzin/infra.git
<div class="diagram">
<h3>Security model</h3>
<table>
<thead><tr><th>Layer</th><th>What</th><th>How</th></tr></thead>
<tbody>
<tr><td>Authentication</td><td>Who are you?</td><td>Authentik SSO (OIDC) → Vault token</td></tr>
<tr><td>Authorization</td><td>What can you access?</td><td>Vault policy (<code>sops-user-*</code>) scoped to your namespaces</td></tr>
<tr><td>Encryption at rest</td><td>State in git</td><td>SOPS + Vault Transit (per-stack key)</td></tr>
<tr><td>Encryption fallback</td><td>Bootstrap / DR</td><td>age keys (admin only)</td></tr>
<tr><td>Network</td><td>Cluster access</td><td>Headscale VPN (private 10.0.20.0/24)</td></tr>
</tbody>
</table>
</div>
</section>
{:else}
<section>
<h3>Step 4 — Clone the repo</h3>
<pre>git clone https://github.com/ViktorBarzin/infra.git
cd infra</pre>
<p>This is where all the infrastructure configuration lives.</p>
</section>
<p>This is where all the infrastructure configuration lives.</p>
</section>
<section>
<h2>Step 5 — Your first change</h2>
<ol>
<li>Create a branch: <pre>git checkout -b my-first-change</pre></li>
<li>Edit a service file (e.g., change an image tag in <code>stacks/echo/main.tf</code>)</li>
<li>Commit and push: <pre>git add . && git commit -m "my first change" && git push -u origin my-first-change</pre></li>
<li>Open a Pull Request on GitHub</li>
<li>Viktor reviews and merges</li>
<li>Woodpecker CI automatically applies the change to the cluster</li>
<li>Slack notification confirms it worked</li>
</ol>
</section>
{/if}
<section>
<h3>Step 5 — Your first change</h3>
<ol>
<li>Create a branch: <pre>git checkout -b my-first-change</pre></li>
<li>Edit a service file (e.g., change an image tag in <code>stacks/echo/main.tf</code>)</li>
<li>Commit and push: <pre>git add . &amp;&amp; git commit -m "my first change" &amp;&amp; git push -u origin my-first-change</pre></li>
<li>Open a Pull Request on GitHub</li>
<li>Viktor reviews and merges</li>
<li>Woodpecker CI automatically applies the change to the cluster</li>
<li>Slack notification confirms it worked</li>
</ol>
</section>
{/if}
</section>
</main>
<style>
.content { max-width: 768px; margin: 2rem auto; padding: 0 1rem; font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; }
.content h1 { border-bottom: 1px solid #e0e0e0; padding-bottom: 0.5rem; }
.content h2 { margin-top: 2rem; color: #333; }
.content h3 { color: #666; margin: 1rem 0 0.25rem; }
.content h3 { color: #444; margin: 1.25rem 0 0.25rem; }
.content h4 { color: #666; margin: 0.75rem 0 0.25rem; }
.content pre { background: #1e1e1e; color: #d4d4d4; padding: 1rem; border-radius: 6px; overflow-x: auto; }
.content pre.output { background: #f5f5f5; color: #333; }
.content code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; }
.content .prereq { font-size: 0.9rem; color: #666; font-style: italic; }
section { margin: 2rem 0; }
.role-tabs { display: flex; gap: 0; margin: 1.5rem 0; border-bottom: 2px solid #e0e0e0; }
section section { margin: 1.25rem 0; }
.path { border-left: 4px solid #4fc3f7; padding-left: 1.25rem; scroll-margin-top: 4rem; }
.path.c { border-left-color: #bbb; }
.badge { display: inline-block; font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; padding: 0.15rem 0.5rem; border-radius: 4px; vertical-align: middle; margin-left: 0.4rem; }
.badge.rec { background: #d4f8d4; color: #1b5e20; }
.badge.none { background: #e3f2fd; color: #0d47a1; }
.role-tabs { display: flex; gap: 0; margin: 1.5rem 0 0.5rem; border-bottom: 2px solid #e0e0e0; }
.role-tabs a { padding: 0.5rem 1.5rem; text-decoration: none; color: #666; border-bottom: 2px solid transparent; margin-bottom: -2px; }
.role-tabs a.active { color: #333; border-bottom-color: #333; font-weight: 600; }
table { border-collapse: collapse; width: 100%; margin: 0.5rem 0; }
@ -258,6 +343,7 @@ cd infra</pre>
.callout { padding: 1rem; border-radius: 6px; margin: 1rem 0; }
.callout.info { background: #e8f4fd; border-left: 4px solid #2196f3; }
.callout.warning { background: #fff3cd; border-left: 4px solid #ffc107; }
.callout ul { margin: 0.5rem 0 0; padding-left: 1.25rem; }
.diagram { background: #fafafa; border: 1px solid #e0e0e0; border-radius: 8px; padding: 1.5rem; margin: 1.5rem 0; }
.diagram h3 { margin: 0 0 1rem 0; color: #333; font-size: 0.95rem; text-transform: uppercase; letter-spacing: 0.5px; }

View file

@ -2,6 +2,19 @@
<h1>Service Catalog</h1>
<p>70+ services running on the cluster. Here are the most commonly used:</p>
<section>
<h2>Cluster Access</h2>
<table>
<thead><tr><th>Service</th><th>URL</th><th>Description</th></tr></thead>
<tbody>
<tr><td>Web Terminal</td><td><a href="https://t3.viktorbarzin.me">t3.viktorbarzin.me</a></td><td>Browser shell on the shared workstation — kubectl, Vault &amp; your repos preinstalled (zero setup)</td></tr>
<tr><td>Kubernetes Dashboard</td><td><a href="https://k8s.viktorbarzin.me">k8s.viktorbarzin.me</a></td><td>Point-and-click view of your workloads, auto-authenticated (zero setup)</td></tr>
<tr><td>Access Portal</td><td><a href="https://k8s-portal.viktorbarzin.me">k8s-portal.viktorbarzin.me</a></td><td>This portal — onboarding, kubeconfig download, setup script</td></tr>
<tr><td>Vault</td><td><a href="https://vault.viktorbarzin.me">vault.viktorbarzin.me</a></td><td>Secrets &amp; dynamic credentials — <code>vault login -method=oidc</code></td></tr>
</tbody>
</table>
</section>
<section>
<h2>Core Services</h2>
<table>
@ -22,7 +35,7 @@
<tbody>
<tr><td>Nextcloud</td><td><a href="https://nextcloud.viktorbarzin.me">nextcloud.viktorbarzin.me</a></td><td>File storage, calendar, contacts</td></tr>
<tr><td>Immich</td><td><a href="https://immich.viktorbarzin.me">immich.viktorbarzin.me</a></td><td>Photo library (Google Photos alternative)</td></tr>
<tr><td>Vaultwarden</td><td><a href="https://vault.viktorbarzin.me">vault.viktorbarzin.me</a></td><td>Password manager</td></tr>
<tr><td>Vaultwarden</td><td><a href="https://vaultwarden.viktorbarzin.me">vaultwarden.viktorbarzin.me</a></td><td>Password manager</td></tr>
<tr><td>Paperless-ngx</td><td><a href="https://pdf.viktorbarzin.me">pdf.viktorbarzin.me</a></td><td>Document management</td></tr>
<tr><td>Navidrome</td><td><a href="https://music.viktorbarzin.me">music.viktorbarzin.me</a></td><td>Music streaming</td></tr>
<tr><td>Tandoor</td><td><a href="https://recipes.viktorbarzin.me">recipes.viktorbarzin.me</a></td><td>Recipe manager</td></tr>

View file

@ -11,6 +11,26 @@
</ol>
</section>
<section>
<h2>Browser login loops, or kubectl says "Unauthorized"</h2>
<p>Command-line SSO (OIDC) login can occasionally be unavailable. You don't have to wait for it — these authenticate a different way and keep working:</p>
<ul>
<li><a href="https://k8s.viktorbarzin.me">Web dashboard</a> — auto-authenticated, no token to paste</li>
<li><a href="https://t3.viktorbarzin.me">Web terminal</a> — its kubectl is already wired up</li>
</ul>
<p>Let Viktor know so the CLI login path gets fixed.</p>
</section>
<section>
<h2>Don't want to set up a local machine at all?</h2>
<p>Skip the VPN and CLI install entirely:</p>
<ul>
<li><a href="https://t3.viktorbarzin.me">t3.viktorbarzin.me</a> — a browser shell with everything preinstalled</li>
<li><a href="https://k8s.viktorbarzin.me">k8s.viktorbarzin.me</a> — a point-and-click dashboard</li>
</ul>
<p>Both just need your Authentik login. See the <a href="/onboarding">Getting Started</a> guide.</p>
</section>
<section>
<h2>"Forbidden" or "Permission denied"</h2>
<p>You may not have access to that namespace. Your access is scoped to specific namespaces.</p>