Skip to content

Latest commit

 

History

History
1951 lines (1735 loc) · 50.7 KB

File metadata and controls

1951 lines (1735 loc) · 50.7 KB

Data Models

Overview

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:

  1. Organization Resources: Multi-tenant organization management with webhook infrastructure
  2. RepoBinding Resources: Custom resources for repository-to-pipeline integration
  3. ArgoCD Applications: GitOps application definitions with sync waves
  4. Authentication Data: User accounts, groups, and OIDC configuration
  5. Certificate Resources: TLS certificates and issuers managed by cert-manager

For detailed architecture, see architecture.md. For operational procedures, see operations.md.

Organization Data Model

Organization Spec

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"  # Optional

Organization Status

interface 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 to org-secrets)
    • RoleBinding: eso-secrets-reader
    • ClusterSecretStore: org-{name}-store

For operational procedures on creating and managing organizations, see operations.md.

Cloudflare Tunnel Credentials

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="
}

DNS Record Model

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 definition
  • platform/crds/organization-crd.yaml - CRD definition
  • platform/platform-controller/controller/controllers/organization_controller.go - Status management and tunnel provisioning

RepoBinding Data Model

RepoBinding Spec

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 string
  • pipelineSpec: 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)

RepoBinding Status

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):

  1. Namespace creation
  2. Pipeline creation (from templateRef)
  3. ServiceAccount creation
  4. RBAC provisioning (Role, RoleBinding, ClusterRole, ClusterRoleBinding, ArgoCD RoleBinding)
  5. AppProject provisioning
  6. Resource limits (ResourceQuota, LimitRange)
  7. Network policy
  8. Terraform backend secret
  9. EventListener namespace update
  10. TriggerTemplate creation
  11. Trigger creation
  12. Allowlist update (legacy, skipped if not found)

Condition Types:

  • NamespaceReady: Pipeline namespace created and configured
  • RBACReady: Service account and RBAC policies configured
  • AppProjectReady: ArgoCD AppProject created for pipeline isolation
  • NetworkPolicyReady: Network isolation policies applied
  • TriggerReady: Tekton webhook handler configured
  • WebhookReady: 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.

KnowledgeBase Data Model

KnowledgeBase Spec

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 string
  • description: Optional, provides context about the knowledge base
  • repositories: Required array with at least one repository
  • repositories[].url: Required, must start with https://github.com/
  • repositories[].branch: Optional, must be valid Git branch name if provided
  • repositories[].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"

KnowledgeBase Status

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 progress
  • Ready: Specification validated, tracking repositories
  • Failed: 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 definition
  • platform/crds/aphex_knowledgebases.yaml - CRD definition
  • platform/platform-controller/controller/controllers/knowledgebase_controller.go - Controller implementation

External Secrets Data Models

ClusterSecretStore Data Model

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-corp

Created By: Organization controller during organization provisioning

Lifecycle: Created when Organization is created, deleted when Organization is deleted

ExternalSecret Data Model

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-password

Created By: Customers in their application namespaces

Requirements:

  • Namespace must have label aphex.dev/org: {organizationName}
  • Source Secret org-secrets must exist in organization namespace
  • ClusterSecretStore must exist for the organization

Source

  • platform/base/external-secrets/kustomization.yaml - External Secrets Operator installation
  • platform/platform-controller/controller/controllers/organization_controller.go - ClusterSecretStore provisioning
  • External Secrets Operator API: https://external-secrets.io/latest/api/externalsecret/

ArgoCD Application Data Model

Application Spec with Sync Waves

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
      };
    };
  };
}

Sync Wave Annotations

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

ArgoCD AppProject Data Model

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 function
  • platform/platform-controller/controller/controllers/repobinding_controller.go - Deletion logic

Authentication Data Model

Authentik User Data

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
}

Authentik Group Data

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 services
  • engineering: Read-only access to platform services

OIDC Provider Configuration

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
}

Dex Configuration

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
}

Certificate Data Model

Certificate Resource

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;
}

ClusterIssuer Resource

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 connector
  • authentik-tls: TLS certificate for Authentik identity provider
  • argocd-tls: TLS certificate for ArgoCD UI
  • tekton-tls: TLS certificate for Tekton Dashboard

Configuration Data Flow

Bootstrap Secret Generation

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
  };
}

Config Sync Job Data

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 definition
  • platform/argocd/apps/ - ArgoCD application definitions
  • platform/auth/authentik/blueprints-configmap.yaml - Authentik configuration
  • platform/auth/dex/dex-config.yaml - Dex configuration
  • platform/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

Application Status

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: ""

EventListener Configuration Data Model

EventListener Spec

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-template

Pipeline Parameters Data Model

Pipeline Parameters

interface 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"

Terraform Backend Configuration Data Model

Terraform Backend Config (Kubernetes Backend)

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
  }
}

Tenant Resource Data Models

Namespace Labels

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"

Resource Quota Spec

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"

LimitRange Spec

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"

NetworkPolicy Spec

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/0

Permission Profile Data Models

Standard Profile

interface 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"]

Elevated Profile

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"]

Data Flow

Onboarding Flow

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

Pipeline Execution Flow

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

GitOps Sync Flow

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

Validation Rules

RepoBinding Validation

  • 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

Namespace Validation

  • 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

Pipeline Parameters Validation

  • repoUrl: Must be valid Git URL
  • commitSha: Must be valid Git commit hash (40 hex characters)
  • tenantName: Must be existing namespace

Authentication Data Models

Platform Groups

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 namespaces

JWT Token Claims

Dex-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"]
}

Authentik User Schema

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": {}
}

Authentik Group Schema

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": {}
}

Authentik OAuth2 Provider Schema

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"
}

Dex Configuration Schema

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"

OIDC Token Claims Schema

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
}

ArgoCD RBAC Policy Model

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]"

Tekton RBAC Model

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.io

Read-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.io

Authentication Secrets Schema

interface 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 encoded

Authentication Data Flow

User Authentication Flow

User 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

Config Sync Job Data Flow

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

Secret Generation Flow

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

Validation Rules

Authentik User Validation

  • username: Must be unique, lowercase, alphanumeric and hyphens
  • email: Must be unique, valid email format
  • name: Required, non-empty string
  • groups: Must reference existing group names

Authentik Group Validation

  • name: Must be unique, non-empty string
  • parent: Must reference existing group UUID (if not null)

Dex Configuration Validation

  • issuer: Must be valid HTTPS URL (or HTTP for internal)
  • staticClients[].id: Must be unique across all clients
  • staticClients[].redirectURIs: Must be valid HTTPS URLs
  • connectors[].config.issuer: Must be valid URL
  • connectors[].config.clientID: Required, non-empty string
  • connectors[].config.clientSecret: Required, non-empty string

OIDC Token Claims Validation

  • iss: Must match Dex issuer URL
  • aud: Must match client ID
  • exp: Must be future timestamp
  • iat: Must be past timestamp
  • email: Must be valid email format
  • groups: Must be array of strings

Source

  • .kiro/specs/dex-authentication-platform/design.md
  • .kiro/specs/dex-authentication-platform/requirements.md
  • platform/auth/authentik/blueprints-configmap.yaml
  • platform/auth/dex/configmap.yaml
  • platform/integrations/argocd-rbac-policy.yaml
  • platform/integrations/tekton-rbac.yaml
  • platform/auth/secrets/README.md

Source

  • .kiro/specs/argocd-tekton-platform/design.md
  • .kiro/specs/argocd-tekton-platform/requirements.md
  • platform/crds/repobinding-crd.yaml
  • platform/platform-controller/controller/
  • platform/tenancy/templates/
  • platform/argocd/apps/