The Aphex Pipeline Infrastructure uses Kubernetes Custom Resource Definitions (CRDs), standard Kubernetes resources, and configuration data structures to manage platform state. All data is stored in Kubernetes etcd with GitOps-managed configuration.
Data flows through the system in four main forms:
- Organization Resources: Multi-tenant organization management with webhook infrastructure
- RepoBinding Resources: Custom resources for repository-to-pipeline integration
- ArgoCD Applications: GitOps application definitions with sync waves
- Authentication Data: User accounts, groups, and OIDC configuration
- Certificate Resources: TLS certificates and issuers managed by cert-manager
For detailed architecture, see architecture.md. For operational procedures, see operations.md.
interface OrganizationSpec {
displayName: string; // Human-readable organization name
adminUsers: string[]; // List of admin email addresses
webhookSecret?: string; // GitHub webhook secret (auto-generated if not provided)
}Validation Rules:
displayName: Required, must match pattern^[a-zA-Z0-9\s\-\.]+$adminUsers: Required array, each email must match pattern^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$webhookSecret: Optional, must match pattern^[a-zA-Z0-9_-]+$if provided
Example:
apiVersion: aphex.io/v1alpha1
kind: Organization
metadata:
name: acme-corp
namespace: platform-system
spec:
displayName: "ACME Corporation"
adminUsers:
- "admin@acme-corp.com"
- "ops@acme-corp.com"
webhookSecret: "my-custom-secret" # Optionalinterface OrganizationStatus {
namespace: string; // Organization namespace (org-{name})
webhookURL: string; // Public webhook URL via Cloudflare tunnel
clusterSecretStore: string; // ClusterSecretStore name (org-{name}-store)
phase: "Pending" | "Active" | "Failed";
message?: string; // Human-readable status message
}Example:
status:
namespace: "org-acme-corp"
webhookURL: "https://acme-corp.arbiter-dev.com"
clusterSecretStore: "org-acme-corp-store"
phase: "Active"Resources Created:
- Organization namespace:
org-{name} - Cloudflare tunnel and DNS record
- EventListener ServiceAccount and ClusterRoleBinding
- Webhook Secret:
github-webhook-secret - External Secrets infrastructure:
- ServiceAccount:
eso-secrets-reader - Role:
eso-secrets-reader(read access toorg-secrets) - RoleBinding:
eso-secrets-reader - ClusterSecretStore:
org-{name}-store
- ServiceAccount:
For operational procedures on creating and managing organizations, see operations.md.
Stored as a Secret in the organization namespace:
interface TunnelCredentials {
AccountTag: string; // Cloudflare account ID
TunnelID: string; // Unique tunnel identifier
TunnelSecret: string; // Base64-encoded tunnel secret
}Secret Name: cloudflared-credentials-{org-name}
Example:
{
"AccountTag": "65671c123fa015f89bfb9f110d0000fd",
"TunnelID": "1280a396-767b-4e77-9425-735765b5f1ae",
"TunnelSecret": "Wl0r8cioWrFIUVb625PfPDOG6rYAgkBkRxzA3QoqGCo="
}Automatically created in Cloudflare for each organization:
interface DNSRecord {
type: "CNAME";
name: string; // {org-name}.arbiter-dev.com
content: string; // {tunnel-id}.cfargotunnel.com
proxied: boolean; // true (enables Cloudflare edge features)
}Example:
{
"type": "CNAME",
"name": "acme-corp.arbiter-dev.com",
"content": "1280a396-767b-4e77-9425-735765b5f1ae.cfargotunnel.com",
"proxied": true
}Source
platform/platform-controller/controller/api/v1alpha1/organization_types.go- Go type definitionplatform/crds/organization-crd.yaml- CRD definitionplatform/platform-controller/controller/controllers/organization_controller.go- Status management and tunnel provisioning
interface RepoBindingSpec {
aphexOrg: string; // Organization name (references Organization resource)
repoOrg: string; // GitHub organization (e.g., "acme-corp")
repoName: string; // Repository name (e.g., "my-application")
pipelineName: string; // Pipeline name to trigger (e.g., "cdktf-deploy-pipeline")
templateRef: string; // Dispatcher template name (e.g., "run-pipeline-v1")
pipelineSpec: string; // Raw YAML content of Tekton Pipeline to create
}Validation Rules:
aphexOrg: Required, must match pattern^[a-z0-9-]+$repoOrg: Required, must match pattern^[a-z0-9-]+$repoName: Required, must match pattern^[a-z0-9-]+$pipelineName: Required, must match pattern^[a-z0-9-]+$templateRef: Required, non-empty stringpipelineSpec: Required, must contain valid Tekton Pipeline YAML
Example:
spec:
aphexOrg: "acme-corp"
repoOrg: "acme-corp"
repoName: "my-application"
pipelineName: "cdktf-deploy-pipeline"
templateRef: "run-pipeline-v1"
pipelineSpec: |
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: cdktf-deploy-pipeline
spec:
params:
- name: git-url
- name: git-revision
tasks:
- name: deploy
taskRef:
name: cdktf-deploy
params:
- name: git-url
value: $(params.git-url)
- name: git-revision
value: $(params.git-revision)interface RepoBindingStatus {
phase: "Pending" | "Provisioning" | "Ready" | "Failed";
message: string;
conditions: Condition[];
webhookConfiguration: {
url: string;
secret: string;
events: string[];
};
provisionedResources: {
namespace: boolean;
pipeline: boolean;
serviceAccount: boolean;
rbac: boolean;
appProject: boolean;
resourceQuota: boolean;
networkPolicy: boolean;
terraformSecret: boolean;
triggerBinding: boolean;
triggerTemplate: boolean;
trigger: boolean;
allowlistUpdated: boolean;
};
}
interface Condition {
type: string;
status: "True" | "False" | "Unknown";
reason: string;
message: string;
lastTransitionTime: string;
}Phase Transitions:
Pending → Provisioning → Ready
↓
Failed
Provisioning Steps (in order):
- Namespace creation
- Pipeline creation (from templateRef)
- ServiceAccount creation
- RBAC provisioning (Role, RoleBinding, ClusterRole, ClusterRoleBinding, ArgoCD RoleBinding)
- AppProject provisioning
- Resource limits (ResourceQuota, LimitRange)
- Network policy
- Terraform backend secret
- EventListener namespace update
- TriggerTemplate creation
- Trigger creation
- Allowlist update (legacy, skipped if not found)
Condition Types:
NamespaceReady: Pipeline namespace created and configuredRBACReady: Service account and RBAC policies configuredAppProjectReady: ArgoCD AppProject created for pipeline isolationNetworkPolicyReady: Network isolation policies appliedTriggerReady: Tekton webhook handler configuredWebhookReady: GitHub webhook configuration available
For operational procedures on creating and managing repo bindings, see operations.md. For API details on RepoBinding resources, see api.md.
interface KnowledgeBaseSpec {
displayName: string; // Human-readable knowledge base name
description?: string; // Optional context about this knowledge base
repositories: Repository[]; // List of repositories to track (minimum 1)
}
interface Repository {
url: string; // GitHub repository URL (https://github.com/org/repo)
branch?: string; // Git branch to track (default: "main")
paths?: string[]; // Documentation paths to track (default: [".kiro/docs"])
}Validation Rules:
displayName: Required, non-empty stringdescription: Optional, provides context about the knowledge baserepositories: Required array with at least one repositoryrepositories[].url: Required, must start withhttps://github.com/repositories[].branch: Optional, must be valid Git branch name if providedrepositories[].paths: Optional, each path must start with.kiro/docs
Example:
apiVersion: aphex.io/v1alpha1
kind: KnowledgeBase
metadata:
name: platform-docs
namespace: platform-system
spec:
displayName: "Platform Documentation"
description: "Archon knowledge base for platform infrastructure and tooling"
repositories:
- url: "https://github.com/bdchatham/AphexPlatformInfrastructure"
branch: "main"
paths:
- ".kiro/docs"
- url: "https://github.com/bdchatham/AphexCLI"
branch: "main"
paths:
- ".kiro/docs"
- url: "https://github.com/bdchatham/ArchonAgent"
branch: "main"
paths:
- ".kiro/docs"interface KnowledgeBaseStatus {
phase: "Pending" | "Ready" | "Failed";
message: string; // Human-readable status information
lastReconcileTime: string; // ISO 8601 timestamp of last reconciliation
}Phase Transitions:
Pending → Ready
↓
Failed
Phase Descriptions:
Pending: Initial state, validation in progressReady: Specification validated, tracking repositoriesFailed: Validation failed, user must fix specification
Example:
status:
phase: "Ready"
message: "Tracking 3 repositories"
lastReconcileTime: "2026-01-16T23:15:00Z"For operational procedures on creating and managing knowledge bases, see operations.md. For API details on KnowledgeBase resources, see api.md.
Source
platform/platform-controller/controller/api/v1alpha1/knowledgebase_types.go- Go type definitionplatform/crds/aphex_knowledgebases.yaml- CRD definitionplatform/platform-controller/controller/controllers/knowledgebase_controller.go- Controller implementation
Purpose: Enables customers to create secrets in their organization namespace and reference them across their systems.
API Version: external-secrets.io/v1
Kind: ClusterSecretStore (cluster-scoped)
Schema:
interface ClusterSecretStore {
apiVersion: "external-secrets.io/v1";
kind: "ClusterSecretStore";
metadata: {
name: string; // Format: "org-{organizationName}-store"
labels: {
"platform.aphex/organization": string;
"platform.aphex/managed-by": "organization-controller";
};
};
spec: {
conditions: Array<{
namespaceSelector: {
matchLabels: {
"aphex.dev/org": string; // Organization name
};
};
}>;
provider: {
kubernetes: {
remoteNamespace: string; // Organization namespace: "org-{name}"
server: {
caProvider: {
type: "ConfigMap";
name: "kube-root-ca.crt";
key: "ca.crt";
namespace: string; // Organization namespace
};
};
auth: {
serviceAccount: {
name: "eso-secrets-reader";
namespace: string; // Organization namespace
};
};
};
};
};
}Example:
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: org-acme-corp-store
labels:
platform.aphex/organization: acme-corp
platform.aphex/managed-by: organization-controller
spec:
conditions:
- namespaceSelector:
matchLabels:
aphex.dev/org: acme-corp
provider:
kubernetes:
remoteNamespace: org-acme-corp
server:
caProvider:
type: ConfigMap
name: kube-root-ca.crt
key: ca.crt
namespace: org-acme-corp
auth:
serviceAccount:
name: eso-secrets-reader
namespace: org-acme-corpCreated By: Organization controller during organization provisioning
Lifecycle: Created when Organization is created, deleted when Organization is deleted
Purpose: Synchronize secrets from organization namespace to target namespaces
API Version: external-secrets.io/v1
Kind: ExternalSecret (namespace-scoped)
Schema:
interface ExternalSecret {
apiVersion: "external-secrets.io/v1";
kind: "ExternalSecret";
metadata: {
name: string;
namespace: string; // Must have label "aphex.dev/org: {organizationName}"
};
spec: {
refreshInterval: string; // e.g., "1h", "5m"
secretStoreRef: {
name: string; // ClusterSecretStore name: "org-{organizationName}-store"
kind: "ClusterSecretStore";
};
target: {
name: string; // Name of Kubernetes Secret to create
creationPolicy: "Owner"; // ExternalSecret owns the Secret
};
data: Array<{
secretKey: string; // Key in target Secret
remoteRef: {
key: "org-secrets"; // Source Secret name (always "org-secrets")
property: string; // Property within source Secret
};
}>;
};
}Example:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: my-app-secrets
namespace: my-app
spec:
refreshInterval: 1h
secretStoreRef:
name: org-acme-corp-store
kind: ClusterSecretStore
target:
name: my-app-secrets
creationPolicy: Owner
data:
- secretKey: github_token
remoteRef:
key: org-secrets
property: github-token
- secretKey: database_password
remoteRef:
key: org-secrets
property: database-passwordCreated By: Customers in their application namespaces
Requirements:
- Namespace must have label
aphex.dev/org: {organizationName} - Source Secret
org-secretsmust exist in organization namespace - ClusterSecretStore must exist for the organization
Source
platform/base/external-secrets/kustomization.yaml- External Secrets Operator installationplatform/platform-controller/controller/controllers/organization_controller.go- ClusterSecretStore provisioning- External Secrets Operator API: https://external-secrets.io/latest/api/externalsecret/
interface ApplicationSpec {
project: string; // ArgoCD project (default: "default")
source: {
repoURL: string; // Git repository URL
targetRevision: string; // Git branch/tag/commit (e.g., "HEAD")
path: string; // Path within repository
};
destination: {
server: string; // Kubernetes API server URL
namespace?: string; // Target namespace (optional)
};
syncPolicy: {
automated: {
prune: boolean; // Delete resources not in Git
selfHeal: boolean; // Correct drift automatically
};
syncOptions: string[]; // Additional sync options
retry: {
limit: number; // Max retry attempts
backoff: {
duration: string; // Initial backoff duration
factor: number; // Backoff multiplier
maxDuration: string; // Maximum backoff duration
};
};
};
}interface SyncWaveAnnotations {
"argocd.argoproj.io/sync-wave": string; // Deployment order (e.g., "10", "20", "30")
}Platform Sync Waves:
- Wave 0:
platform-ingress-controller- Ingress controller deployment - Wave 1:
platform-tekton- Tekton Pipelines and Triggers - Wave 3:
platform-gpu(k3s only) - GPU Operator - Wave 5:
platform-crds,platform-rbac- CRDs and RBAC policies - Wave 10:
platform-cert-manager- cert-manager installation - Wave 15:
platform-external-secrets- External Secrets Operator - Wave 20:
platform-cert-foundation,platform-auth,platform-controllers,platform-catalog- Certificates, auth, and controllers - Wave 30:
platform-ingress,platform-gateway(k3s only),platform-external-dns(k3s only) - Ingress and networking
Purpose: Enforce logical security boundaries for customer team resources in ArgoCD.
API Version: argoproj.io/v1alpha1
Kind: AppProject (namespace-scoped in argocd namespace)
Schema:
interface AppProject {
apiVersion: "argoproj.io/v1alpha1";
kind: "AppProject";
metadata: {
name: string; // Pipeline name
namespace: "argocd";
labels: {
"platform.aphex/pipeline": string;
"platform.aphex/managed-by": "platform-controller";
};
};
spec: {
description: string; // e.g., "Project for {pipelineName} pipeline"
destinations: Array<{
server: "https://kubernetes.default.svc";
namespace: string; // "{pipelineName}" or "{pipelineName}-*"
}>;
sourceRepos: string[]; // Allowed Git repositories
clusterResourceWhitelist: []; // Empty (no cluster resources allowed)
namespaceResourceWhitelist: Array<{
group: "*";
kind: "*";
}>;
};
}Example:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: my-pipeline
namespace: argocd
labels:
platform.aphex/pipeline: my-pipeline
platform.aphex/managed-by: platform-controller
spec:
description: "Project for my-pipeline pipeline"
destinations:
- server: https://kubernetes.default.svc
namespace: my-pipeline
- server: https://kubernetes.default.svc
namespace: my-pipeline-*
sourceRepos:
- https://github.com/my-org/my-repo
- https://github.com/my-org/my-repo.git
clusterResourceWhitelist: []
namespaceResourceWhitelist:
- group: '*'
kind: '*'Created By: RepoBinding controller during RBAC provisioning
Lifecycle:
- Created when RepoBinding is created
- Updated if RepoBinding spec changes (repo URL, pipeline name)
- Deleted when RepoBinding is deleted (with finalizer cleanup)
Security Constraints:
- Destinations: Only
{pipelineName}and{pipelineName}-*namespaces - Source Repositories: Only the specific GitHub repository
- Cluster Resources: None (empty whitelist)
- Namespace Resources: All resources within scoped namespaces
Source
platform/platform-controller/controller/controllers/repobinding_provisioners.go- provisionArgoCDAppProject functionplatform/platform-controller/controller/controllers/repobinding_controller.go- Deletion logic
interface AuthentikUser {
pk: number; // Primary key
username: string; // Username
email: string; // Email address
name: string; // Display name
is_active: boolean; // Account active status
groups: number[]; // Group membership (by group PK)
attributes: Record<string, any>; // Custom attributes
}interface AuthentikGroup {
pk: number; // Primary key
name: string; // Group name (e.g., "admins", "engineering")
is_superuser: boolean; // Superuser privileges
users: number[]; // User membership (by user PK)
attributes: Record<string, any>; // Custom attributes
}Platform Groups:
admins: Full access to all platform servicesengineering: Read-only access to platform services
interface OIDCProvider {
name: string; // Provider name (e.g., "dex")
client_id: string; // OIDC client ID
client_secret: string; // OIDC client secret
authorization_url: string; // Authorization endpoint
access_token_url: string; // Token endpoint
profile_url: string; // User info endpoint
oidc_jwks_url: string; // JWKS endpoint
issuer: string; // OIDC issuer URL
}interface DexConfig {
issuer: string; // Dex issuer URL (e.g., "https://dex.home.local")
storage: {
type: "kubernetes";
config: {
inCluster: boolean;
};
};
web: {
http: string; // HTTP listen address
tlsCert?: string; // TLS certificate path
tlsKey?: string; // TLS key path
};
connectors: DexConnector[];
staticClients: DexClient[];
oauth2: {
skipApprovalScreen: boolean;
};
}
interface DexConnector {
type: "oidc";
id: string; // Connector ID
name: string; // Display name
config: {
issuer: string; // Authentik issuer URL
clientID: string; // OIDC client ID
clientSecret: string; // OIDC client secret
redirectURI: string; // Redirect URI
scopes: string[]; // OIDC scopes
claimsMapping: {
groups: string; // Groups claim name
};
};
}
interface DexClient {
id: string; // Client ID (e.g., "argocd", "tekton-dashboard")
redirectURIs: string[]; // Allowed redirect URIs
name: string; // Client display name
secret: string; // Client secret
}interface Certificate {
apiVersion: "cert-manager.io/v1";
kind: "Certificate";
metadata: {
name: string; // Certificate name (e.g., "dex-tls")
namespace: string; // Target namespace
};
spec: {
secretName: string; // Secret name for certificate storage
issuerRef: {
name: string; // Issuer name (e.g., "selfsigned-issuer")
kind: "ClusterIssuer" | "Issuer";
};
dnsNames: string[]; // DNS names for certificate
duration?: string; // Certificate validity duration
renewBefore?: string; // Renewal threshold
};
status?: {
conditions: CertificateCondition[];
renewalTime?: string;
};
}
interface CertificateCondition {
type: "Ready" | "Issuing";
status: "True" | "False" | "Unknown";
reason: string;
message: string;
lastTransitionTime: string;
}interface ClusterIssuer {
apiVersion: "cert-manager.io/v1";
kind: "ClusterIssuer";
metadata: {
name: string; // Issuer name (e.g., "selfsigned-issuer")
};
spec: {
selfSigned?: {}; // Self-signed issuer configuration
acme?: { // ACME/Let's Encrypt configuration
server: string; // ACME server URL
email: string; // Contact email
privateKeySecretRef: {
name: string; // Secret for ACME private key
};
solvers: ACMESolver[];
};
};
status?: {
conditions: IssuerCondition[];
acme?: {
uri: string;
lastRegisteredEmail: string;
};
};
}Platform Certificates:
dex-tls: TLS certificate for Dex OIDC connectorauthentik-tls: TLS certificate for Authentik identity providerargocd-tls: TLS certificate for ArgoCD UItekton-tls: TLS certificate for Tekton Dashboard
interface GeneratedSecrets {
postgresql: {
password: string; // PostgreSQL user password
postgresPassword: string; // PostgreSQL superuser password
};
authentik: {
secretKey: string; // Authentik SECRET_KEY
adminPassword: string; // Admin user password
bootstrapToken: string; // API token for automation
};
dex: {
clientSecret: string; // OIDC client secret
};
argocd: {
oidcClientSecret: string; // ArgoCD OIDC client secret
};
tekton: {
oidcClientSecret: string; // Tekton Dashboard OIDC client secret
};
}interface ConfigSyncJobData {
authentikConfig: {
baseUrl: string; // Authentik base URL
token: string; // API token
providerId: number; // OIDC provider ID
};
dexConfig: {
clientSecret: string; // Client secret to update
namespace: string; // Dex deployment namespace
deploymentName: string; // Dex deployment name
};
validation: {
authentikDiscovery: string; // Authentik OIDC discovery URL
dexDiscovery: string; // Dex OIDC discovery URL
};
}Source
platform/crds/repobinding-crd.yaml- RepoBinding CRD definitionplatform/argocd/apps/- ArgoCD application definitionsplatform/auth/authentik/blueprints-configmap.yaml- Authentik configurationplatform/auth/dex/dex-config.yaml- Dex configurationplatform/cert-foundation/certificates.yaml- Certificate definitions }; syncPolicy: { automated?: { prune: boolean; // Delete resources not in Git selfHeal: boolean; // Revert manual changes }; retry?: { limit: number; // Max retry attempts backoff: { duration: string; // Initial backoff duration factor: number; // Backoff multiplier maxDuration: string; // Max backoff duration }; }; }; }
**Example**:
```yaml
spec:
project: default
source:
repoURL: https://github.com/bdchatham/AphexPlatformInfrastructure
targetRevision: main
path: platform/argocd/apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
interface ApplicationStatus {
sync: {
status: "Synced" | "OutOfSync" | "Unknown";
revision: string; // Git commit SHA
};
health: {
status: "Healthy" | "Progressing" | "Degraded" | "Suspended" | "Missing" | "Unknown";
};
conditions: Array<{
type: string;
status: string;
message: string;
}>;
}Example:
status:
sync:
status: Synced
revision: abc123def456
health:
status: Healthy
conditions:
- type: ComparisonError
status: "False"
message: ""interface EventListenerSpec {
serviceAccountName: string; // Service account for pipeline execution
triggers: Array<{
name: string; // Trigger name
interceptors: Array<{
ref: {
name: string; // Interceptor type (e.g., "github", "cel")
};
params: Array<{
name: string;
value: any;
}>;
}>;
bindings: Array<{
ref: string; // TriggerBinding name
}>;
template: {
ref: string; // TriggerTemplate name
};
}>;
}Example:
spec:
serviceAccountName: pipeline-runner
triggers:
- name: github-push
interceptors:
- ref:
name: github
params:
- name: secretRef
value:
secretName: webhook-archon
secretKey: secret
- name: eventTypes
value:
- push
- ref:
name: cel
params:
- name: filter
value: "body.ref == 'refs/heads/main'"
bindings:
- ref: github-push-binding
template:
ref: cdktf-deploy-trigger-templateinterface PipelineParams {
repoUrl: string; // Git repository URL
commitSha: string; // Git commit SHA to build
tenantName: string; // Tenant namespace for RBAC context
}Example:
params:
- name: repo-url
value: "https://github.com/your-github-org/archon-agent"
- name: commit-sha
value: "abc123def456..."
- name: tenant-name
value: "archon"interface TerraformBackendConfig {
backend: "kubernetes";
secretSuffix: string; // Tenant name for state isolation
namespace: string; // Tenant namespace
inClusterConfig: boolean; // Use in-cluster credentials
}Example:
terraform {
backend "kubernetes" {
secret_suffix = "archon"
namespace = "archon"
in_cluster_config = true
}
}interface NamespaceLabels {
"aphex/tenant": string; // Tenant name
"aphex/repo": string; // Repository (org/name)
"aphex/managed-by": string; // "platform-controller"
}Example:
labels:
aphex/tenant: "archon"
aphex/repo: "your-github-org/archon-agent"
aphex/managed-by: "platform-controller"interface ResourceQuotaSpec {
hard: {
"requests.cpu": string; // e.g., "4"
"requests.memory": string; // e.g., "8Gi"
"limits.cpu": string; // e.g., "8"
"limits.memory": string; // e.g., "16Gi"
"persistentvolumeclaims": string; // e.g., "5"
"pods": string; // e.g., "20"
};
}Example:
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
persistentvolumeclaims: "5"
pods: "20"interface LimitRangeSpec {
limits: Array<{
type: "Container";
default: {
cpu: string; // e.g., "500m"
memory: string; // e.g., "512Mi"
};
defaultRequest: {
cpu: string; // e.g., "100m"
memory: string; // e.g., "128Mi"
};
}>;
}Example:
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"interface NetworkPolicySpec {
podSelector: {}; // Empty selector matches all pods
policyTypes: ["Ingress", "Egress"];
ingress: Array<{
from: Array<{
podSelector?: {};
namespaceSelector?: {
matchLabels: { [key: string]: string };
};
}>;
ports?: Array<{
protocol: string;
port: number;
}>;
}>;
egress: Array<{
to: Array<{
podSelector?: {};
namespaceSelector?: {
matchLabels: { [key: string]: string };
};
ipBlock?: {
cidr: string;
except?: string[];
};
}>;
ports?: Array<{
protocol: string;
port: number;
}>;
}>;
}Example:
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector: {}
egress:
- to:
- podSelector: {}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- to:
- ipBlock:
cidr: 0.0.0.0/0interface StandardProfile {
role: {
rules: Array<{
apiGroups: string[];
resources: string[];
verbs: string[];
}>;
};
}Example:
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "configmaps", "secrets"]
verbs: ["get", "list", "create", "update", "delete"]
- apiGroups: ["tekton.dev"]
resources: ["pipelineruns", "taskruns"]
verbs: ["get", "list", "create"]interface ElevatedProfile {
role: {
rules: Array<{
apiGroups: string[];
resources: string[];
verbs: string[];
}>;
};
}Example:
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "configmaps", "secrets", "services", "persistentvolumeclaims"]
verbs: ["get", "list", "create", "update", "delete"]
- apiGroups: ["tekton.dev"]
resources: ["pipelineruns", "taskruns", "pipelines", "tasks"]
verbs: ["get", "list", "create", "update", "delete"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets"]
verbs: ["get", "list", "create", "update", "delete"]User creates RepoBinding
↓
Onboarding Controller reconciles
↓
Validate spec (org, repo, namespace, profile)
↓
Generate webhook secret
↓
Create Namespace with labels
↓
Create ServiceAccount
↓
Create Role (based on profile)
↓
Create RoleBinding
↓
Create ResourceQuota
↓
Create LimitRange
↓
Create NetworkPolicy
↓
Create Terraform backend secret
↓
Create EventListener
↓
Create Ingress
↓
Update RepoBinding status to Ready
GitHub webhook event
↓
Ingress routes to EventListener
↓
EventListener validates webhook signature
↓
EventListener checks CEL filter (main branch)
↓
EventListener creates PipelineRun in tenant namespace
↓
PipelineRun executes as tenant ServiceAccount
↓
Pipeline pods run with RBAC constraints
↓
Pipeline accesses Terraform state via secret
↓
Pipeline completes, logs stored
Engineer commits platform changes to Git
↓
ArgoCD polls Git repository (every 3 minutes)
↓
ArgoCD detects changes
↓
ArgoCD compares Git state with cluster state
↓
ArgoCD applies changes to cluster
↓
ArgoCD updates Application status
aphexOrg: Must match^[a-z0-9-]+$repoOrg: Must match^[a-z0-9-]+$repoName: Must match^[a-z0-9-]+$pipelineName: Must match^[a-z0-9-]+$templateRef: Required, non-empty string
- Name must be valid DNS label (lowercase alphanumeric and hyphens)
- Name cannot be privileged (kube-system, pipeline-system, argocd, tekton-pipelines, etc.)
- Name must be unique in cluster
repoUrl: Must be valid Git URLcommitSha: Must be valid Git commit hash (40 hex characters)tenantName: Must be existing namespace
The authentication system defines three platform groups with specific permissions:
# platform-admins group
name: platform-admins
is_superuser: true
permissions:
- Full CRUD access to all platform CRDs
- Create and delete namespaces
- Read logs and events in all namespaces
# platform-operators group
name: platform-operators
is_superuser: false
permissions:
- Full CRUD access to platform CRDs (no namespace management)
- Read logs and events in all namespaces
- Cannot create or delete namespaces
# platform-engineering group
name: platform-engineering
is_superuser: false
permissions:
- Create, read, update platform CRDs (no delete)
- Restricted to user-* and team-* namespaces only
- Cannot access platform system namespacesDex-issued JWT tokens contain the following claims structure:
interface JWTClaims {
iss: string; // Issuer: "https://dex.home.local"
sub: string; // Subject: unique user identifier
aud: string; // Audience: "kubernetes" | "argocd" | "tekton-dashboard"
exp: number; // Expiration time (Unix timestamp)
iat: number; // Issued at time (Unix timestamp)
email: string; // User's email address (used as username)
email_verified: boolean; // Whether email is verified
name: string; // User's display name
groups: string[]; // User's group memberships from Authentik
}Example Kubernetes API Token:
{
"iss": "https://dex.home.local",
"sub": "CgVhbGljZRIEbW9jaw",
"aud": "kubernetes",
"exp": 1704153600,
"iat": 1704067200,
"email": "alice@platform.local",
"email_verified": true,
"name": "Alice Developer",
"groups": ["platform-engineering"]
}interface AuthentikUser {
pk: number; // Primary key (unique user ID)
username: string; // Username (unique, lowercase)
name: string; // Display name
email: string; // Email address (unique)
is_active: boolean; // Whether user is active
is_superuser: boolean; // Whether user has superuser permissions
last_login: string; // ISO 8601 timestamp of last login
groups: string[]; // Array of group names
attributes: { // Custom user attributes
[key: string]: any;
};
}Example:
{
"pk": 1,
"username": "admin",
"name": "Admin User",
"email": "admin@example.com",
"is_active": true,
"is_superuser": true,
"last_login": "2024-01-05T10:00:00Z",
"groups": ["admins"],
"attributes": {}
}interface AuthentikGroup {
pk: string; // Primary key (UUID)
name: string; // Group name (unique)
is_superuser: boolean; // Whether group has superuser permissions
parent: string | null; // Parent group UUID (null if top-level)
users: number[]; // Array of user PKs
attributes: { // Custom group attributes
[key: string]: any;
};
}Example:
{
"pk": "abc123-def456-ghi789",
"name": "admins",
"is_superuser": false,
"parent": null,
"users": [1, 2],
"attributes": {}
}interface AuthentikOAuth2Provider {
pk: number; // Primary key
name: string; // Provider name
authorization_flow: string; // Authorization flow UUID
client_type: "confidential" | "public";
client_id: string; // OAuth2 client ID
client_secret: string; // OAuth2 client secret (masked in responses)
redirect_uris: string; // Newline-separated redirect URIs
signing_key: string; // Signing key UUID
access_code_validity: string; // Access code validity duration (e.g., "minutes=1")
access_token_validity: string;// Access token validity duration (e.g., "minutes=5")
refresh_token_validity: string;// Refresh token validity duration (e.g., "days=30")
include_claims_in_id_token: boolean;
issuer_mode: "global" | "per_provider";
sub_mode: "hashed_user_id" | "user_id" | "user_username" | "user_email";
}Example:
{
"pk": 1,
"name": "Dex OIDC Provider",
"authorization_flow": "abc123-def456",
"client_type": "confidential",
"client_id": "dex-client",
"client_secret": "***",
"redirect_uris": "https://dex.home.local/callback",
"signing_key": "ghi789-jkl012",
"access_code_validity": "minutes=1",
"access_token_validity": "minutes=5",
"refresh_token_validity": "days=30",
"include_claims_in_id_token": true,
"issuer_mode": "per_provider",
"sub_mode": "hashed_user_id"
}interface DexConfig {
issuer: string; // Dex issuer URL (e.g., "https://dex.home.local")
storage: {
type: "kubernetes";
config: {
inCluster: boolean; // Use in-cluster Kubernetes credentials
};
};
web: {
http: string; // HTTP listen address (e.g., "0.0.0.0:5556")
};
logger: {
level: "debug" | "info" | "warn" | "error";
format: "json" | "text";
};
staticClients: Array<{
id: string; // Client ID
name: string; // Client display name
secretEnv: string; // Environment variable containing client secret
redirectURIs: string[]; // Allowed redirect URIs
}>;
connectors: Array<{
type: "oidc";
id: string; // Connector ID
name: string; // Connector display name
config: {
issuer: string; // Upstream OIDC issuer URL
clientID: string; // Client ID for upstream provider
clientSecret: string; // Client secret for upstream provider
redirectURI: string; // Dex callback URL
scopes: string[]; // Requested scopes
getUserInfo: boolean; // Fetch user info from userinfo endpoint
insecureSkipEmailVerified: boolean;
insecureEnableGroups: boolean;
claimMapping: {
groups: string; // Claim name for groups
};
};
}>;
expiry: {
signingKeys: string; // Signing key rotation interval (e.g., "6h")
idTokens: string; // ID token validity (e.g., "24h")
refreshTokens: {
validIfNotUsedFor: string;// Refresh token idle timeout (e.g., "2160h")
absoluteLifetime: string; // Refresh token absolute lifetime (e.g., "3960h")
};
};
}Example:
issuer: https://dex.home.local
storage:
type: kubernetes
config:
inCluster: true
web:
http: 0.0.0.0:5556
logger:
level: info
format: json
staticClients:
- id: argocd
name: ArgoCD
secretEnv: ARGOCD_CLIENT_SECRET
redirectURIs:
- https://argocd.home.local/auth/callback
- id: tekton-dashboard
name: Tekton Dashboard
secretEnv: TEKTON_CLIENT_SECRET
redirectURIs:
- https://tekton.home.local/auth/callback
connectors:
- type: oidc
id: authentik
name: Authentik
config:
issuer: http://authentik.auth-system.svc.cluster.local:9000/application/o/platform-services/
clientID: dex-client
clientSecret: $AUTHENTIK_CLIENT_SECRET
redirectURI: https://dex.home.local/callback
scopes:
- openid
- profile
- email
- groups
getUserInfo: true
insecureSkipEmailVerified: true
insecureEnableGroups: true
claimMapping:
groups: groups
expiry:
signingKeys: "6h"
idTokens: "24h"
refreshTokens:
validIfNotUsedFor: "2160h"
absoluteLifetime: "3960h"interface OIDCTokenClaims {
iss: string; // Issuer (Dex URL)
sub: string; // Subject (unique user ID)
aud: string; // Audience (client ID)
exp: number; // Expiration time (Unix timestamp)
iat: number; // Issued at time (Unix timestamp)
name: string; // User's display name
email: string; // User's email address
groups: string[]; // User's group memberships
email_verified: boolean; // Whether email is verified
}Example:
{
"iss": "https://dex.home.local",
"sub": "abc123",
"aud": "argocd",
"exp": 1704542400,
"iat": 1704456000,
"name": "John Doe",
"email": "john.doe@example.com",
"groups": ["admins"],
"email_verified": true
}interface ArgoCDRBACPolicy {
policy: {
csv: string; // CSV-formatted RBAC policy
};
scopes: string; // RBAC scopes (e.g., "[groups]")
}Policy CSV Format:
p, role:admin, applications, *, */*, allow
p, role:admin, clusters, *, *, allow
p, role:admin, repositories, *, *, allow
p, role:readonly, applications, get, */*, allow
p, role:readonly, clusters, get, *, allow
p, role:readonly, repositories, get, *, allow
g, admins, role:admin
g, engineering, role:readonly
Policy Rules:
p: Permission rule (role, resource, action, object, effect)g: Group mapping (group, role)
Example:
policy.csv: |
p, role:admin, applications, *, */*, allow
p, role:admin, clusters, *, *, allow
p, role:admin, repositories, *, *, allow
p, role:readonly, applications, get, */*, allow
p, role:readonly, clusters, get, *, allow
p, role:readonly, repositories, get, *, allow
g, admins, role:admin
g, engineering, role:readonly
scopes: "[groups]"interface TektonRBACModel {
clusterRole: {
rules: Array<{
apiGroups: string[];
resources: string[];
verbs: string[];
}>;
};
clusterRoleBinding: {
subjects: Array<{
kind: "Group";
name: string; // Group name from OIDC token
apiGroup: "rbac.authorization.k8s.io";
}>;
roleRef: {
kind: "ClusterRole";
name: string;
apiGroup: "rbac.authorization.k8s.io";
};
};
}Admin Role Example:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tekton-admin
rules:
- apiGroups: ["tekton.dev"]
resources: ["*"]
verbs: ["*"]
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tekton-admin-binding
subjects:
- kind: Group
name: admins
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: tekton-admin
apiGroup: rbac.authorization.k8s.ioRead-Only Role Example:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tekton-readonly
rules:
- apiGroups: ["tekton.dev"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tekton-readonly-binding
subjects:
- kind: Group
name: engineering
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: tekton-readonly
apiGroup: rbac.authorization.k8s.iointerface AuthenticationSecrets {
"authentik-postgresql": {
"postgresql-password": string; // Database password (base64)
"postgresql-postgres-password": string;// Superuser password (base64)
};
"authentik-secrets": {
"secret-key": string; // Authentik secret key (base64)
"admin-password": string; // Admin password (base64)
};
"dex-secrets": {
"client-secret": string; // Dex client secret (base64)
};
"authentik-api-token": {
"token": string; // API token (base64)
};
}Example:
# authentik-postgresql Secret
apiVersion: v1
kind: Secret
metadata:
name: authentik-postgresql
namespace: auth-system
type: Opaque
data:
postgresql-password: YWJjMTIzZGVmNDU2 # base64 encoded
postgresql-postgres-password: eHl6Nzg5Z2hpMDEy # base64 encoded
---
# authentik-secrets Secret
apiVersion: v1
kind: Secret
metadata:
name: authentik-secrets
namespace: auth-system
type: Opaque
data:
secret-key: ZGVmNDU2amtsNzg5bW5vMzQ1cHFyOTAxc3R1MjM0dnd4NTY3eXphYjY3OGNkZTkwMQ== # base64 encoded
admin-password: Z2hpNzg5bW5vMzQ1 # base64 encoded
---
# dex-secrets Secret
apiVersion: v1
kind: Secret
metadata:
name: dex-secrets
namespace: auth-system
type: Opaque
data:
client-secret: amtsMTIzbW5vNDU2 # base64 encoded
---
# authentik-api-token Secret
apiVersion: v1
kind: Secret
metadata:
name: authentik-api-token
namespace: auth-system
type: Opaque
data:
token: cHFyOTAxc3R1MjM0dnd4NTY3 # base64 encodedUser accesses ArgoCD/Tekton Dashboard
↓
Service redirects to Dex authorization endpoint
↓
Dex redirects to Authentik login page
↓
User enters credentials in Authentik
↓
Authentik validates credentials against PostgreSQL
↓
Authentik returns authorization code to Dex
↓
Dex exchanges code for tokens from Authentik
↓
Dex returns authorization code to service
↓
Service exchanges code for tokens from Dex
↓
Service validates ID token and extracts claims
↓
Service grants access based on groups claim
Bootstrap creates Authentik API token
↓
Bootstrap stores token in authentik-api-token Secret
↓
Config Sync Job reads token from Secret
↓
Job waits for Authentik to be ready
↓
Job fetches OAuth2 providers from Authentik API
↓
Job finds Dex OIDC provider by name/client_id/redirect_uris
↓
Job reads Dex client secret from dex-secrets Secret
↓
Job updates Dex OIDC provider with client secret via API
↓
Job verifies Authentik OIDC discovery endpoint
↓
Job scales Dex deployment to 1 replica
↓
Job waits for Dex to be ready
↓
Job verifies Dex OIDC discovery endpoint
↓
Authentication system is operational
Bootstrap script starts
↓
Generate PostgreSQL password (32 bytes random)
↓
Generate Authentik secret key (50 bytes random)
↓
Generate Authentik admin password (32 bytes random)
↓
Generate Dex client secret (32 bytes random)
↓
Create Kubernetes Secrets in auth-system namespace
↓
ArgoCD deploys Authentik with secrets
↓
Bootstrap waits for Authentik to be ready
↓
Bootstrap authenticates to Authentik API
↓
Bootstrap creates API token via Authentik API
↓
Bootstrap stores API token in authentik-api-token Secret
↓
Config Sync Job uses API token to configure Authentik
username: Must be unique, lowercase, alphanumeric and hyphensemail: Must be unique, valid email formatname: Required, non-empty stringgroups: Must reference existing group names
name: Must be unique, non-empty stringparent: Must reference existing group UUID (if not null)
issuer: Must be valid HTTPS URL (or HTTP for internal)staticClients[].id: Must be unique across all clientsstaticClients[].redirectURIs: Must be valid HTTPS URLsconnectors[].config.issuer: Must be valid URLconnectors[].config.clientID: Required, non-empty stringconnectors[].config.clientSecret: Required, non-empty string
iss: Must match Dex issuer URLaud: Must match client IDexp: Must be future timestampiat: Must be past timestampemail: Must be valid email formatgroups: Must be array of strings
Source
.kiro/specs/dex-authentication-platform/design.md.kiro/specs/dex-authentication-platform/requirements.mdplatform/auth/authentik/blueprints-configmap.yamlplatform/auth/dex/configmap.yamlplatform/integrations/argocd-rbac-policy.yamlplatform/integrations/tekton-rbac.yamlplatform/auth/secrets/README.md
Source
.kiro/specs/argocd-tekton-platform/design.md.kiro/specs/argocd-tekton-platform/requirements.mdplatform/crds/repobinding-crd.yamlplatform/platform-controller/controller/platform/tenancy/templates/platform/argocd/apps/