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> <main>
<h1>Kubernetes Access Portal</h1> <h1>Kubernetes Access Portal</h1>
<div class="callout warning"> <div class="callout info">
<strong>VPN Required</strong> — The cluster is on a private network. You need Headscale VPN access before kubectl will work. <strong>Fastest way in:</strong> open the <a href="https://t3.viktorbarzin.me">web terminal</a> or the
<a href="/onboarding">See the Getting Started guide</a> for VPN setup instructions. <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> </div>
<section> <section>
@ -26,6 +28,7 @@
<p><strong>Assigned namespaces:</strong> {data.namespaces.join(', ')}</p> <p><strong>Assigned namespaces:</strong> {data.namespaces.join(', ')}</p>
<h3>Quick Commands</h3> <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> <pre>
# Check your pods # Check your pods
kubectl get pods -n {data.namespaces[0]} kubectl get pods -n {data.namespaces[0]}
@ -47,16 +50,23 @@ vault write kubernetes/creds/{data.namespaces[0]}-deployer \
<section> <section>
<h2>Get Started</h2> <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> <ol>
{#if data.role === 'namespace-owner'} {#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} {: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} {/if}
<li><a href="/setup">Install kubectl and kubelogin</a></li> <li><a href="/setup">Install kubectl and kubelogin</a></li>
<li><a href="/download">Download your kubeconfig</a></li> <li><a href="/download">Download your kubeconfig</a></li>
<li>Run <code>kubectl get namespaces</code> to verify access</li> <li>Run <code>kubectl get namespaces</code> to verify access</li>
</ol> </ol>
<p><a href="/onboarding">Compare all three access paths →</a></p>
</section> </section>
<section> <section>
@ -91,12 +101,12 @@ vault write kubernetes/creds/{data.namespaces[0]}-deployer \
border-radius: 6px; border-radius: 6px;
margin: 1rem 0; margin: 1rem 0;
} }
.callout.warning { .callout.info {
background: #fff3cd; background: #e8f4fd;
border-left: 4px solid #ffc107; border-left: 4px solid #2196f3;
} }
.callout a { .callout a {
color: #856404; color: #0d47a1;
font-weight: 600; font-weight: 600;
} }
</style> </style>

View file

@ -5,22 +5,123 @@
<main class="content"> <main class="content">
<h1>Getting Started</h1> <h1>Getting Started</h1>
<p>Welcome! Follow these steps to get access to the home Kubernetes cluster.</p> <p>
Welcome! There are three ways to reach the home Kubernetes cluster. Pick the one that fits —
<div class="role-tabs"> the first two need <strong>zero setup</strong> and open right in your browser.
<a href="/onboarding" class:active={!showNamespaceOwner}>General User</a> </p>
<a href="/onboarding?role=namespace-owner" class:active={showNamespaceOwner}>Namespace Owner</a>
</div>
<section> <section>
<h2>Step 0 — Join the VPN</h2> <h2>Three ways in</h2>
<p>The cluster is on a private network (<code>10.0.20.0/24</code>). You need VPN access first.</p> <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>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>
</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 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 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>
<section>
<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> <ol>
<li>Install <a href="https://tailscale.com/download" target="_blank">Tailscale</a> for your OS</li> <li>Install <a href="https://tailscale.com/download" target="_blank">Tailscale</a> for your OS</li>
<li>Run this in your terminal: <li>Run this in your terminal:
<pre>tailscale login --login-server https://headscale.viktorbarzin.me</pre> <pre>tailscale login --login-server https://headscale.viktorbarzin.me</pre>
</li> </li>
<li>A browser window will open with a registration URL</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>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>Wait for approval (usually within a few hours)</li>
<li>Once approved, test: <pre>ping 10.0.20.100</pre></li> <li>Once approved, test: <pre>ping 10.0.20.100</pre></li>
@ -28,62 +129,49 @@
</section> </section>
<section> <section>
<h2>Step 1 — Log in to the portal</h2> <h3>Step 2 — Install the tools</h3>
<p>Visit <a href="https://k8s-portal.viktorbarzin.me">k8s-portal.viktorbarzin.me</a> and sign in with your Authentik account.</p> <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>
<p>If you don't have an account yet, ask Viktor to create one.</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>
<section> <section>
<h2>Step 2 — Set up kubectl</h2> <h3>Step 3 — Verify access</h3>
<p>Run one of these commands in your terminal to install everything automatically:</p> <p>Run this. The first time, it opens your browser for SSO login:</p>
<h3>macOS</h3> <pre>kubectl get {showNamespaceOwner ? 'pods -n YOUR_NAMESPACE' : 'namespaces'}</pre>
<p class="prereq">Requires <a href="https://brew.sh" target="_blank">Homebrew</a>. Install it first if you don't have it.</p> <p>You should see your resources (or an empty list if you haven't deployed anything yet).</p>
<pre>bash &lt;(curl -fsSL https://k8s-portal.viktorbarzin.me/setup/script?os=mac)</pre> <div class="callout warning">
<h3>Linux</h3> <strong>Browser login loops, or kubectl says "Unauthorized"?</strong> Command-line SSO
<pre>bash &lt;(curl -fsSL https://k8s-portal.viktorbarzin.me/setup/script?os=linux)</pre> (OIDC) can occasionally be unavailable. When that happens, use the
<h3>Windows</h3> <a href="#path-dashboard">web dashboard (Path B)</a> or the
<p>Use <a href="https://learn.microsoft.com/en-us/windows/wsl/install" target="_blank">WSL2</a> and follow the Linux instructions.</p> <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> </section>
{#if showNamespaceOwner} {#if showNamespaceOwner}
<section> <section>
<h2>Step 3 — Log into Vault</h2> <h3>Step 4 — Log into Vault</h3>
<p>Vault manages your secrets and issues dynamic Kubernetes credentials.</p> <p>Vault manages your secrets and issues dynamic Kubernetes credentials.</p>
<pre>vault login -method=oidc</pre> <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> <p>This opens your browser for Authentik SSO. After login, your token is saved to <code>~/.vault-token</code>.</p>
</section> </section>
<section> <section>
<h2>Step 4 — Verify kubectl access</h2> <h3>Step 5 — Clone the infra repo</h3>
<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>
</section>
<section>
<h2>Step 5 — Clone the infra repo</h2>
<pre>git clone https://github.com/ViktorBarzin/infra.git <pre>git clone https://github.com/ViktorBarzin/infra.git
cd infra</pre> cd infra</pre>
<p>This is where all the infrastructure configuration lives. Terraform state is committed as encrypted files.</p> <p>This is where all the infrastructure configuration lives. Terraform state is committed as encrypted files.</p>
</section> </section>
<section> <section>
<h2>Step 6 — Install tools</h2> <h3>Step 6 — Decrypt your state</h3>
<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> <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 <pre># Make sure you're logged into Vault
vault login -method=oidc vault login -method=oidc
@ -132,7 +220,7 @@ cd stacks/YOUR_NAMESPACE
</section> </section>
<section> <section>
<h2>Step 8 — Create your first app stack</h2> <h3>Step 7 — Create your first app stack</h3>
<ol> <ol>
<li>Copy the template: <pre>cp -r stacks/_template stacks/myapp <li>Copy the template: <pre>cp -r stacks/_template stacks/myapp
mv stacks/myapp/main.tf.example stacks/myapp/main.tf</pre></li> mv stacks/myapp/main.tf.example stacks/myapp/main.tf</pre></li>
@ -153,7 +241,7 @@ git push</pre>
</section> </section>
<section> <section>
<h2>Architecture Overview</h2> <h3>Architecture Overview</h3>
<p>Here's how your changes flow through the system:</p> <p>Here's how your changes flow through the system:</p>
<div class="diagram"> <div class="diagram">
@ -204,31 +292,18 @@ git push</pre>
</section> </section>
{:else} {:else}
<section> <section>
<h2>Step 3 — Verify access</h2> <h3>Step 4 — Clone the repo</h3>
<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 <pre>git clone https://github.com/ViktorBarzin/infra.git
cd infra</pre> cd infra</pre>
<p>This is where all the infrastructure configuration lives.</p> <p>This is where all the infrastructure configuration lives.</p>
</section> </section>
<section> <section>
<h2>Step 5 — Your first change</h2> <h3>Step 5 — Your first change</h3>
<ol> <ol>
<li>Create a branch: <pre>git checkout -b my-first-change</pre></li> <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>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>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>Open a Pull Request on GitHub</li>
<li>Viktor reviews and merges</li> <li>Viktor reviews and merges</li>
<li>Woodpecker CI automatically applies the change to the cluster</li> <li>Woodpecker CI automatically applies the change to the cluster</li>
@ -236,19 +311,29 @@ cd infra</pre>
</ol> </ol>
</section> </section>
{/if} {/if}
</section>
</main> </main>
<style> <style>
.content { max-width: 768px; margin: 2rem auto; padding: 0 1rem; font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; } .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 h1 { border-bottom: 1px solid #e0e0e0; padding-bottom: 0.5rem; }
.content h2 { margin-top: 2rem; color: #333; } .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 { 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 code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; }
.content .prereq { font-size: 0.9rem; color: #666; font-style: italic; } .content .prereq { font-size: 0.9rem; color: #666; font-style: italic; }
section { margin: 2rem 0; } 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 { 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; } .role-tabs a.active { color: #333; border-bottom-color: #333; font-weight: 600; }
table { border-collapse: collapse; width: 100%; margin: 0.5rem 0; } 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 { padding: 1rem; border-radius: 6px; margin: 1rem 0; }
.callout.info { background: #e8f4fd; border-left: 4px solid #2196f3; } .callout.info { background: #e8f4fd; border-left: 4px solid #2196f3; }
.callout.warning { background: #fff3cd; border-left: 4px solid #ffc107; } .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 { 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; } .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> <h1>Service Catalog</h1>
<p>70+ services running on the cluster. Here are the most commonly used:</p> <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> <section>
<h2>Core Services</h2> <h2>Core Services</h2>
<table> <table>
@ -22,7 +35,7 @@
<tbody> <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>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>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>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>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> <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> </ol>
</section> </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> <section>
<h2>"Forbidden" or "Permission denied"</h2> <h2>"Forbidden" or "Permission denied"</h2>
<p>You may not have access to that namespace. Your access is scoped to specific namespaces.</p> <p>You may not have access to that namespace. Your access is scoped to specific namespaces.</p>