Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions e2e/smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const ROUTES: RouteSpec[] = [
{ path: "/adventures/echoes-lost-in-orbit/levels/beginner", title: /Broken Echoes/ },
{ path: "/adventures/echoes-lost-in-orbit/levels/intermediate", title: /The Silent Canary/ },
{ path: "/adventures/echoes-lost-in-orbit/levels/expert", title: /Hyperspace Operations & Transport/ },
{ path: "/adventures/lex-imperfecta", title: /Lex Imperfecta/ },
{ path: "/adventures/lex-imperfecta/levels/beginner", title: /The Twelve Tables/ },
{ path: "/adventures/the-ai-observatory", title: /The AI Observatory/ },
{ path: "/adventures/the-ai-observatory/levels/beginner", title: /Calibrating the Lens/ },
{ path: "/adventures/the-ai-observatory/levels/intermediate", title: /The Distracted Pilot/ },
Expand All @@ -41,6 +43,8 @@ const ROUTES: RouteSpec[] = [
{ path: "/challenges/grafana", title: /Grafana Challenges/ },
{ path: "/challenges/jaeger", title: /Jaeger Challenges/ },
{ path: "/challenges/java", title: /Java Challenges/ },
{ path: "/challenges/kubernetes", title: /Kubernetes Challenges/ },
{ path: "/challenges/kyverno", title: /Kyverno Challenges/ },
{ path: "/challenges/openfeature", title: /OpenFeature Challenges/ },
{ path: "/challenges/openllmetry", title: /OpenLLMetry Challenges/ },
{ path: "/challenges/opentelemetry", title: /OpenTelemetry Challenges/ },
Expand Down
4 changes: 4 additions & 0 deletions public/sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<url><loc>https://offon.dev/adventures/echoes-lost-in-orbit/levels/beginner/</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
<url><loc>https://offon.dev/adventures/echoes-lost-in-orbit/levels/intermediate/</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
<url><loc>https://offon.dev/adventures/echoes-lost-in-orbit/levels/expert/</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
<url><loc>https://offon.dev/adventures/lex-imperfecta/</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
<url><loc>https://offon.dev/adventures/lex-imperfecta/levels/beginner/</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
<url><loc>https://offon.dev/adventures/the-ai-observatory/</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
<url><loc>https://offon.dev/adventures/the-ai-observatory/levels/beginner/</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
<url><loc>https://offon.dev/adventures/the-ai-observatory/levels/intermediate/</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
Expand All @@ -31,6 +33,8 @@
<url><loc>https://offon.dev/challenges/grafana/</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>
<url><loc>https://offon.dev/challenges/jaeger/</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>
<url><loc>https://offon.dev/challenges/java/</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>
<url><loc>https://offon.dev/challenges/kubernetes/</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>
<url><loc>https://offon.dev/challenges/kyverno/</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>
<url><loc>https://offon.dev/challenges/openfeature/</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>
<url><loc>https://offon.dev/challenges/openllmetry/</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>
<url><loc>https://offon.dev/challenges/opentelemetry/</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>
Expand Down
4 changes: 4 additions & 0 deletions react-router.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export default {
"/adventures/echoes-lost-in-orbit/levels/beginner",
"/adventures/echoes-lost-in-orbit/levels/intermediate",
"/adventures/echoes-lost-in-orbit/levels/expert",
"/adventures/lex-imperfecta",
"/adventures/lex-imperfecta/levels/beginner",
"/adventures/the-ai-observatory",
"/adventures/the-ai-observatory/levels/beginner",
"/adventures/the-ai-observatory/levels/intermediate",
Expand All @@ -41,6 +43,8 @@ export default {
"/challenges/grafana",
"/challenges/jaeger",
"/challenges/java",
"/challenges/kubernetes",
"/challenges/kyverno",
"/challenges/openfeature",
"/challenges/openllmetry",
"/challenges/opentelemetry",
Expand Down
1 change: 1 addition & 0 deletions scripts/refresh-leaderboard.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const ADVENTURE_CATEGORIES = {
"blind-by-design": { categoryId: 41, has_beginner: true, has_intermediate: true, has_expert: true, has_single: false },
"building-cloudhaven": { categoryId: 36, has_beginner: true, has_intermediate: true, has_expert: true, has_single: false },
"echoes-lost-in-orbit": { categoryId: 35, has_beginner: true, has_intermediate: true, has_expert: true, has_single: false },
"lex-imperfecta": { categoryId: 0, has_beginner: true, has_intermediate: false, has_expert: false, has_single: false }, // TODO: set categoryId — look up at https://community.offon.dev/categories.json
"the-ai-observatory": { categoryId: 37, has_beginner: true, has_intermediate: true, has_expert: true, has_single: false },
// /GENERATED:adventures
};
Expand Down
2 changes: 2 additions & 0 deletions src/data/adventures/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BLIND_BY_DESIGN } from "./blind-by-design.generated";
import { BUILDING_CLOUDHAVEN } from "./building-cloudhaven.generated";
import { ECHOES_LOST_IN_ORBIT } from "./echoes-lost-in-orbit.generated";
import { LEX_IMPERFECTA } from "./lex-imperfecta.generated";
import { THE_AI_OBSERVATORY } from "./the-ai-observatory.generated";
import type { Adventure, AdventureContributor, RelatedLevel } from "./types";

Expand All @@ -10,6 +11,7 @@ export const ADVENTURES: Adventure[] = [
BLIND_BY_DESIGN,
BUILDING_CLOUDHAVEN,
ECHOES_LOST_IN_ORBIT,
LEX_IMPERFECTA,
THE_AI_OBSERVATORY,
];

Expand Down
140 changes: 140 additions & 0 deletions src/data/adventures/lex-imperfecta.generated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { CODESPACES_BASE, COMMUNITY_URL } from "@/data/constants";
import type { Adventure } from "./types";

export const LEX_IMPERFECTA: Adventure = {
id: "lex-imperfecta",
title: "Lex Imperfecta",
icon: "Scale",
month: "MAY 2026",
story: "The Roman Republic has built a sophisticated legal system to protect its citizens — but the laws were written in haste, and the exceptions were written too generously. Policies go unenforced, the wrong citizens are exempt, and something has slipped through the gates unnoticed. As a newly appointed Praetor, your mission is to restore order before chaos takes hold.",
tags: ["Kyverno", "Kubernetes"],
backstory: [
"The Roman Republic has built a sophisticated legal system to protect its citizens — but the laws were written in haste, and the exceptions were written too generously. Policies go unenforced, the wrong citizens are exempt, and something has slipped through the gates unnoticed. As a newly appointed Praetor, your mission is to restore order before chaos takes hold.",
],
overview: [
"The Republic's legal system is in disarray — workloads run unchecked, required labels go missing, and privileged containers slip through the gates. As a newly appointed Praetor, your mission is to restore order by fixing broken Kyverno policies and enforcing proper admission control.",
],
rewards: {
deadline: "",
eligibility: "Complete all levels and post your solution in the community before the deadline to be eligible.",
tiers: [
{ label: "1st place", description: "50% voucher for a Linux Foundation certification" },
{ label: "Top 3", description: "Credly badge to showcase the achievement" },
],
rankingNote: "Ranking is determined by total points across all three levels. Points per level are awarded by submission order within the active week (100 for the first valid solution, 95 for the second, and so on; late submissions still earn 60).",
rankingRulesUrl: `${COMMUNITY_URL}/t/about-the-challenges-category/16`,
},
upcomingLevels: [
{ name: "Intermediate", difficulty: "Intermediate" },
{ name: "Expert", difficulty: "Expert" },
],
levels: [
{
id: "beginner",
name: "The Twelve Tables",
difficulty: "Beginner",
topics: ["Kyverno", "Kubernetes"],
audience: `Platform engineers, SREs, and developers curious about Kubernetes security — no prior Kyverno experience needed, but familiarity with basic \`kubectl\` and YAML will help.`,
learnings: [
"How Kyverno [ValidatingPolicy](https://kyverno.io/docs/policy-types/validating-policy/) resources and [CEL validation expressions](https://kubernetes.io/docs/reference/using-api/cel/) work",
"The difference between [Audit, Deny, and Warn](https://kyverno.io/docs/policy-types/validating-policy/) validation actions",
"How to use [custom label keys](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) to enforce workload identity standards",
"How Kyverno [MutatingPolicy](https://kyverno.io/docs/policy-types/mutating-policy/) resources automatically patch incoming workloads at admission",
],
codespacesUrl: `${CODESPACES_BASE}?devcontainer_path=.devcontainer%2Flex-imperfecta_beginner%2Fdevcontainer.json&quickstart=1`,
discussionUrl: "",
intro: ["Fix broken Kyverno policies to restore proper admission control."],
backstory: [
"The Republic's legal scholars have been busy — perhaps too busy. In their haste to codify the Twelve Tables, the foundation of the Republic's legal system, they introduced errors that now threaten the city's order. Workloads that should be blocked are running freely, and workloads that should be allowed are being turned away at the gates.",
"Another scholar left a note: \"I tried to set up policies for privileged containers and required labels, but something's off — I can't figure out why the wrong things are getting through. There was also supposed to be a system for automatically issuing travel permits to foreign visitors, but that one is broken too.\"",
"Your mission: investigate the Kyverno policies and restore proper admission control before chaos reaches the city.",
],
objective: [
`All workloads **missing the \`republic.rome/gens\` label** blocked at admission with a clear policy violation message`,
"All workloads **running as privileged containers** blocked at admission with a clear policy violation message",
`All pods declaring **\`republic.rome/traveler: peregrinus\`** automatically receiving the **\`republic.rome/travel-permit: granted\`** label`,
"Confirmed that **all other workloads** deploy and run successfully in the cluster",
],
architecture: [
"The defining principle of the Twelve Tables was that Roman law was enforced **at the gates** — before a citizen could act, not after the damage was done. Kubernetes admission control works exactly the same way: Kyverno intercepts every request to create or update a workload and checks it against your policies *before* it reaches the cluster. A misconfigured policy doesn't just fail to enforce — it fails silently, letting non-compliant workloads slip through unnoticed while you assume everything is fine.",
`That's the situation you've inherited. Your Codespace comes with a Kubernetes cluster and Kyverno pre-installed. Three policies are already deployed — two \`ValidatingPolicy\` resources that validate workloads, and one \`MutatingPolicy\` that automatically stamps incoming pods with the right labels. All three are misconfigured. The policies live in \`manifests/policies/\`. You will edit them directly and re-apply with \`kubectl\`.`,
`The pods in \`manifests/pods/\` are there for reference only — **you don't need to edit them**.`,
"No GitOps, no dashboards — just you, the policies, and the cluster.",
],
toolbox: [
{ name: "kubectl", description: "Apply and inspect cluster resources", url: "https://kubernetes.io/docs/reference/kubectl/" },
{ name: "kyverno CLI", description: "Test and lint policies locally before applying", url: "https://kyverno.io/docs/kyverno-cli/" },
{ name: "k9s", description: "Explore cluster resources in a terminal UI", url: "https://k9scli.io/" },
],
howToPlay: [
{ title: "Explore the Cluster", content: `When your Codespace is ready, four pods are already running — or trying to. Open a terminal and check what's going on:

\`\`\`bash
kubectl get pods
\`\`\`

Inspect why a pod was blocked or admitted:

\`\`\`bash
kubectl describe pod <pod-name>
\`\`\`

Check the policies that are in place:

\`\`\`bash
kubectl get validatingpolicies
kubectl get validatingpolicy require-labels -o yaml
kubectl get validatingpolicy no-privileged-containers -o yaml

kubectl get mutatingpolicies
kubectl get mutatingpolicy stamp-travel-permit -o yaml
\`\`\`

You can also launch **k9s** for a terminal UI view of all cluster resources:

\`\`\`bash
k9s
\`\`\`

Navigate to \`ValidatingPolicy\` resources with \`:validatingpolicies\` and \`MutatingPolicy\` resources with \`:mutatingpolicies\` to inspect all three policies.
` },
{ title: "Fix the Policies", content: `Review the [Objective](#objective) and investigate what's wrong in \`manifests/policies/\`.

All three broken policies are in \`manifests/policies/\`. Read them carefully — each has a different kind of misconfiguration.

**Test Locally with the Kyverno CLI**

Before applying to the cluster, you can use the \`kyverno\` CLI to test your policy changes locally against the workload manifests:

\`\`\`bash
kyverno apply manifests/policies/require-labels.yaml --resource manifests/pods/missing-labels.yaml
kyverno apply manifests/policies/no-privileged-containers.yaml --resource manifests/pods/privileged.yaml
kyverno apply manifests/policies/stamp-travel-permit.yaml --resource manifests/pods/peregrinus.yaml
\`\`\`

This gives you fast feedback without touching the cluster.

**Apply to the Cluster**

Once you're happy with your changes, re-apply everything:

\`\`\`bash
make apply
\`\`\`

This re-applies the policies and re-deploys all workloads so you immediately see the effect of your changes.
` },
],
helpfulLinks: [
{ title: "Kyverno ValidatingPolicy", url: "https://kyverno.io/docs/policy-types/validating-policy/", description: "Reference docs for ValidatingPolicy — the resource type you'll fix to block non-compliant workloads" },
{ title: "Kyverno MutatingPolicy", url: "https://kyverno.io/docs/policy-types/mutating-policy/", description: "Reference docs for MutatingPolicy — the resource type you'll fix to auto-stamp travel permits" },
{ title: "CEL Validation Expressions", url: "https://kubernetes.io/docs/reference/using-api/cel/", description: "How CEL expressions work in Kubernetes admission — what you'll write inside the policy rules" },
{ title: "Kyverno Playground", url: "https://playground.kyverno.io", description: "Test your CEL expressions interactively against sample resources before applying them to the cluster" },
],
verification: {
command: "./verify.sh",
description: "Once you think you've solved the challenge, run the verification script. If it fails it will tell you which checks didn't pass. If it passes, it generates a Certificate of Completion you can paste into the discussion.",
},
},
],
};
Loading
Loading