legionio 1.4.79 → 1.4.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +11 -0
  3. data/.github/workflow-templates/eval-gate.yml +118 -0
  4. data/.github/workflows/ci-cd.yml +40 -0
  5. data/.github/workflows/ci.yml +25 -0
  6. data/CHANGELOG.md +52 -0
  7. data/Dockerfile +26 -8
  8. data/deploy/helm/legion/Chart.yaml +6 -0
  9. data/deploy/helm/legion/templates/_helpers.tpl +16 -0
  10. data/deploy/helm/legion/templates/deployment-api.yaml +55 -0
  11. data/deploy/helm/legion/templates/deployment-worker.yaml +46 -0
  12. data/deploy/helm/legion/templates/hpa-worker.yaml +22 -0
  13. data/deploy/helm/legion/templates/pdb.yaml +13 -0
  14. data/deploy/helm/legion/templates/service-api.yaml +15 -0
  15. data/deploy/helm/legion/templates/serviceaccount.yaml +8 -0
  16. data/deploy/helm/legion/values.yaml +69 -0
  17. data/lib/legion/api/governance.rb +80 -0
  18. data/lib/legion/api/org_chart.rb +41 -0
  19. data/lib/legion/api/workflow.rb +46 -0
  20. data/lib/legion/api.rb +8 -0
  21. data/lib/legion/cli/acp_command.rb +46 -0
  22. data/lib/legion/cli/chat_command.rb +85 -0
  23. data/lib/legion/cli/check/privacy_check.rb +86 -0
  24. data/lib/legion/cli/check_command.rb +46 -0
  25. data/lib/legion/cli/dashboard/renderer.rb +20 -0
  26. data/lib/legion/cli/eval_command.rb +217 -0
  27. data/lib/legion/cli/init/config_generator.rb +22 -0
  28. data/lib/legion/cli/lex_cli_manifest.rb +67 -0
  29. data/lib/legion/cli/lex_command.rb +101 -0
  30. data/lib/legion/cli.rb +14 -1
  31. data/lib/legion/extensions.rb +66 -0
  32. data/lib/legion/helpers/context.rb +62 -0
  33. data/lib/legion/service.rb +23 -0
  34. data/lib/legion/version.rb +1 -1
  35. data/public/governance/index.html +284 -0
  36. data/public/workflow/index.html +216 -0
  37. metadata +23 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 833830caa5613e077d49c9a67c1dc00b907a473daf7be9033c9389c51546a5e3
4
- data.tar.gz: 9c93197f8b0a9ecd0f784598c9b81e98d0158ab03b94d4b822a3d6ef55419868
3
+ metadata.gz: 512139af5d950a28790627a762f8dc5f52e37cd093ed2829a9a42aa833ededec
4
+ data.tar.gz: f80a9bfa077caa791bf68058c91eb1061b9cf3851c1766ef5aedd49e9ecda95e
5
5
  SHA512:
6
- metadata.gz: 4689fa56cc6ad0cfd06f1b836fd36211edeaedacc65306ed888bdc97d36e1d430f5c7c6f3eb65427ff7c234cf1908f2fb14f26f448b8d6f2a414c726011c699f
7
- data.tar.gz: 3a127117aba989a57d1e1077e6972a2d32d07c02148fb53eae5c083e8752d509d4a9d84a9db9bb3086292a983e3154d8632e7bca41483a5e9740ccb1769c1ec0
6
+ metadata.gz: ecd74de131841cb6a00d1fe66ccfa51fe0a0eafe649e0cdf26e90c8602127f0d29d7f55b4cfeda0f13908b0eebe9828d61dc380a7518268e29bda49bd780855d
7
+ data.tar.gz: 9d27ac10782d1e6319e96b517c6da5a819e7b599f15a2e9ae27a5d1f3e33f13d70838bd64390218931ded9be7ec45c3cc28e1e65dc2cedebcbd5faf17fa1ba6a
data/.dockerignore ADDED
@@ -0,0 +1,11 @@
1
+ .git
2
+ .github
3
+ spec
4
+ docs
5
+ *.md
6
+ .rubocop.yml
7
+ .rspec
8
+ tmp
9
+ log
10
+ .dockerignore
11
+ Dockerfile
@@ -0,0 +1,118 @@
1
+ # .github/workflow-templates/eval-gate.yml
2
+ #
3
+ # Eval gate workflow template for LegionIO CI/CD pipelines.
4
+ # Copy this file to .github/workflows/eval-gate.yml in your repo and adjust the
5
+ # env vars to match your dataset and threshold requirements.
6
+ #
7
+ # Required secrets:
8
+ # LEGIONIO_BOOTSTRAP_CONFIG (base64-encoded bootstrap JSON, or omit for defaults)
9
+ #
10
+ # Usage:
11
+ # - Trigger manually (workflow_dispatch) or on push/PR targeting main
12
+ # - Job exits 0 if avg_score >= threshold, exits 1 and fails the pipeline if below
13
+
14
+ name: Eval Gate
15
+
16
+ on:
17
+ push:
18
+ branches: [main]
19
+ pull_request:
20
+ branches: [main]
21
+ workflow_dispatch:
22
+ inputs:
23
+ dataset:
24
+ description: 'Dataset name to evaluate'
25
+ required: true
26
+ default: 'default'
27
+ threshold:
28
+ description: 'Pass/fail threshold (0.0 - 1.0)'
29
+ required: false
30
+ default: '0.8'
31
+ evaluator:
32
+ description: 'Evaluator name (leave blank for first builtin template)'
33
+ required: false
34
+ default: ''
35
+
36
+ env:
37
+ DATASET: ${{ github.event.inputs.dataset || 'default' }}
38
+ THRESHOLD: ${{ github.event.inputs.threshold || '0.8' }}
39
+ EVALUATOR: ${{ github.event.inputs.evaluator || '' }}
40
+
41
+ jobs:
42
+ eval-gate:
43
+ name: Eval Gate (${{ env.DATASET }} @ ${{ env.THRESHOLD }})
44
+ runs-on: ubuntu-latest
45
+
46
+ steps:
47
+ - name: Checkout
48
+ uses: actions/checkout@v4
49
+
50
+ - name: Set up Ruby
51
+ uses: ruby/setup-ruby@v1
52
+ with:
53
+ ruby-version: '3.4'
54
+ bundler-cache: true
55
+
56
+ - name: Install Legion
57
+ run: gem install legionio --no-document
58
+
59
+ - name: Bootstrap config (optional)
60
+ if: ${{ secrets.LEGIONIO_BOOTSTRAP_CONFIG != '' }}
61
+ env:
62
+ LEGIONIO_BOOTSTRAP_CONFIG: ${{ secrets.LEGIONIO_BOOTSTRAP_CONFIG }}
63
+ run: echo "Bootstrap config present"
64
+
65
+ - name: Run eval gate
66
+ id: eval
67
+ env:
68
+ LEGIONIO_BOOTSTRAP_CONFIG: ${{ secrets.LEGIONIO_BOOTSTRAP_CONFIG }}
69
+ run: |
70
+ EVAL_ARGS="--dataset $DATASET --threshold $THRESHOLD --exit-code --json"
71
+ if [ -n "$EVALUATOR" ]; then
72
+ EVAL_ARGS="$EVAL_ARGS --evaluator $EVALUATOR"
73
+ fi
74
+ legion eval run $EVAL_ARGS | tee eval-report.json
75
+
76
+ - name: Upload eval report
77
+ if: always()
78
+ uses: actions/upload-artifact@v4
79
+ with:
80
+ name: eval-report-${{ github.run_number }}
81
+ path: eval-report.json
82
+ retention-days: 30
83
+
84
+ - name: Annotate PR with eval results
85
+ if: github.event_name == 'pull_request' && always()
86
+ uses: actions/github-script@v7
87
+ with:
88
+ script: |
89
+ const fs = require('fs');
90
+ let report;
91
+ try {
92
+ report = JSON.parse(fs.readFileSync('eval-report.json', 'utf8'));
93
+ } catch (e) {
94
+ console.log('Could not parse eval report:', e.message);
95
+ return;
96
+ }
97
+ const gate = report.passed ? 'PASSED' : 'FAILED';
98
+ const score = (report.avg_score || 0).toFixed(3);
99
+ const thresh = report.threshold || 0;
100
+ const body = [
101
+ `## Eval Gate: ${gate}`,
102
+ '',
103
+ `| Metric | Value |`,
104
+ `|--------|-------|`,
105
+ `| Dataset | \`${report.dataset}\` |`,
106
+ `| Evaluator | \`${report.evaluator}\` |`,
107
+ `| Avg Score | ${score} |`,
108
+ `| Threshold | ${thresh} |`,
109
+ `| Total Rows | ${report.summary?.total ?? 'N/A'} |`,
110
+ `| Passed | ${report.summary?.passed ?? 'N/A'} |`,
111
+ `| Failed | ${report.summary?.failed ?? 'N/A'} |`,
112
+ ].join('\n');
113
+ github.rest.issues.createComment({
114
+ issue_number: context.issue.number,
115
+ owner: context.repo.owner,
116
+ repo: context.repo.repo,
117
+ body: body,
118
+ });
@@ -0,0 +1,40 @@
1
+ name: CI/CD
2
+ on:
3
+ pull_request:
4
+ branches: [main]
5
+
6
+ jobs:
7
+ test:
8
+ name: Test
9
+ runs-on: ubuntu-latest
10
+ services:
11
+ rabbitmq:
12
+ image: rabbitmq:3.13-management
13
+ ports: ['5672:5672']
14
+ postgres:
15
+ image: postgres:16
16
+ env:
17
+ POSTGRES_PASSWORD: test
18
+ POSTGRES_DB: legion_test
19
+ ports: ['5432:5432']
20
+ options: >-
21
+ --health-cmd pg_isready
22
+ --health-interval 10s
23
+ --health-timeout 5s
24
+ --health-retries 5
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: '3.4'
30
+ bundler-cache: true
31
+ - run: bundle install && bundle exec rspec
32
+ - run: bundle exec rubocop
33
+
34
+ helm-lint:
35
+ name: Helm Lint
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+ - uses: azure/setup-helm@v3
40
+ - run: helm lint deploy/helm/legion
@@ -27,3 +27,28 @@ jobs:
27
27
  gh api repos/LegionIO/homebrew-tap/dispatches \
28
28
  -f event_type=build-daemon \
29
29
  -f "client_payload[legionio_version]=${{ needs.release.outputs.version }}"
30
+
31
+ docker-build:
32
+ name: Build Docker Image
33
+ needs: release
34
+ if: needs.release.outputs.changed == 'true'
35
+ runs-on: ubuntu-latest
36
+ permissions:
37
+ packages: write
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ - uses: docker/setup-buildx-action@v3
41
+ - uses: docker/login-action@v3
42
+ with:
43
+ registry: ghcr.io
44
+ username: ${{ github.actor }}
45
+ password: ${{ secrets.GITHUB_TOKEN }}
46
+ - uses: docker/build-push-action@v5
47
+ with:
48
+ context: .
49
+ push: true
50
+ tags: |
51
+ ghcr.io/legionio/legion:${{ needs.release.outputs.version }}
52
+ ghcr.io/legionio/legion:latest
53
+ cache-from: type=gha
54
+ cache-to: type=gha,mode=max
data/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.4.85] - 2026-03-20
4
+
5
+ ### Added
6
+ - `legion lex fixes` CLI command to list pending auto-fix patches (filterable by status)
7
+ - `legion lex approve-fix FIX_ID` CLI command to approve LLM-generated fixes
8
+ - `legion lex reject-fix FIX_ID` CLI command to reject LLM-generated fixes
9
+ - `with_data` helper to `legion lex` subcommand class for data-required operations
10
+
11
+ ## [1.4.84] - 2026-03-20
12
+
13
+ ### Added
14
+ - `Legion::Extensions.load_yaml_agents` — loads YAML/JSON agent definitions from `~/.legionio/agents/` or configured directory
15
+ - `generate_yaml_runner` — dynamically generates a runner Module for each agent with `llm`, `script`, and `http` function types
16
+ - YAML agent loading integrated into `hook_extensions` boot sequence
17
+ - Governance API routes under `/api/governance/approvals` (list, show, submit, approve, reject)
18
+ - HTML governance dashboard at `/governance/` with approve/reject buttons, 30s auto-poll, and reviewer dialog
19
+ - Static file serving enabled for `public/` directory in Sinatra
20
+
21
+ ## [1.4.83] - 2026-03-20
22
+
23
+ ### Added
24
+ - `Helpers::Context` for filesystem-based inter-agent context sharing
25
+ - Org chart API endpoint (`GET /api/org-chart`) with dashboard panel
26
+ - Workflow relationship graph API (`GET /api/relationships/graph`)
27
+ - Workflow visualizer web page (`public/workflow/`) with Cytoscape.js
28
+ - `--worktree` flag for `legion chat` with auto-checkpointing
29
+ - `.legion-context/` and `.legion-worktrees/` in generated `.gitignore`
30
+
31
+ ## [1.4.82] - 2026-03-20
32
+
33
+ ### Added
34
+ - `legion check --privacy` command: verifies enterprise privacy mode (flag set, no cloud API keys, external endpoints unreachable)
35
+ - `PrivacyCheck` class with three probes: flag_set, no_cloud_keys, no_external_endpoints
36
+ - `Legion::Service.log_privacy_mode_status` logs enterprise privacy state at startup
37
+
38
+ ## [1.4.81] - 2026-03-20
39
+
40
+ ### Added
41
+ - `legion eval experiments` subcommand: list all experiment runs with status and summary
42
+ - `legion eval promote --experiment NAME --tag TAG` subcommand: tag a prompt version for production via lex-prompt
43
+ - `legion eval compare --run1 NAME --run2 NAME` subcommand: side-by-side diff of two experiment runs
44
+ - `require_prompt!` guard for lex-prompt extension availability
45
+
46
+ ## [1.4.80] - 2026-03-20
47
+
48
+ ### Added
49
+ - `legion eval run` CLI subcommand for CI/CD threshold-based eval gating
50
+ - `--dataset`, `--threshold`, `--evaluator`, `--exit-code` options on `eval run`
51
+ - JSON report output to stdout with per-row scores, summary, and timestamp
52
+ - `.github/workflow-templates/eval-gate.yml` reusable GitHub Actions workflow template
53
+ - PR annotation step in workflow template for inline eval result comments
54
+
3
55
  ## [1.4.79] - 2026-03-20
4
56
 
5
57
  ### Added
data/Dockerfile CHANGED
@@ -1,9 +1,27 @@
1
- FROM ruby:3.4-alpine
2
- LABEL maintainer="Matthew Iverson <matthewdiverson@gmail.com>"
1
+ # Build stage
2
+ FROM ruby:3.4-slim AS builder
3
+ WORKDIR /app
4
+ RUN apt-get update && \
5
+ apt-get install -y --no-install-recommends build-essential libpq-dev git && \
6
+ rm -rf /var/lib/apt/lists/*
7
+ COPY Gemfile legionio.gemspec ./
8
+ COPY lib/legion/version.rb lib/legion/
9
+ RUN bundle lock && \
10
+ bundle config set --local without 'development test' && \
11
+ bundle install --jobs 4 --retry 3
12
+ COPY . .
3
13
 
4
- RUN mkdir /etc/legionio
5
- RUN apk update && apk add build-base postgresql-dev mysql-client mariadb-dev tzdata gcc git
6
-
7
- COPY . ./
8
- RUN gem install legionio tzinfo-data tzinfo --no-document --no-prerelease
9
- CMD ruby --yjit $(which legion)
14
+ # Runtime stage
15
+ FROM ruby:3.4-slim AS runtime
16
+ RUN apt-get update && \
17
+ apt-get install -y --no-install-recommends libpq5 curl && \
18
+ rm -rf /var/lib/apt/lists/* && \
19
+ groupadd -r legion && useradd -r -g legion -d /app -s /sbin/nologin legion
20
+ WORKDIR /app
21
+ COPY --from=builder --chown=legion:legion /app /app
22
+ USER legion
23
+ EXPOSE 4567
24
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
25
+ CMD curl -sf http://localhost:4567/api/health || exit 1
26
+ ENTRYPOINT ["bundle", "exec"]
27
+ CMD ["legion", "start"]
@@ -0,0 +1,6 @@
1
+ apiVersion: v2
2
+ name: legion
3
+ description: LegionIO async job engine
4
+ version: 0.1.0
5
+ appVersion: "1.4.13"
6
+ type: application
@@ -0,0 +1,16 @@
1
+ {{- define "legion.fullname" -}}
2
+ {{- .Release.Name }}-legion
3
+ {{- end }}
4
+
5
+ {{- define "legion.labels" -}}
6
+ app.kubernetes.io/name: legion
7
+ app.kubernetes.io/instance: {{ .Release.Name }}
8
+ app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
9
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
10
+ helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
11
+ {{- end }}
12
+
13
+ {{- define "legion.selectorLabels" -}}
14
+ app.kubernetes.io/name: legion
15
+ app.kubernetes.io/instance: {{ .Release.Name }}
16
+ {{- end }}
@@ -0,0 +1,55 @@
1
+ apiVersion: apps/v1
2
+ kind: Deployment
3
+ metadata:
4
+ name: {{ include "legion.fullname" . }}-api
5
+ labels:
6
+ {{- include "legion.labels" . | nindent 4 }}
7
+ app.kubernetes.io/component: api
8
+ spec:
9
+ replicas: {{ .Values.api.replicas }}
10
+ selector:
11
+ matchLabels:
12
+ {{- include "legion.selectorLabels" . | nindent 6 }}
13
+ app.kubernetes.io/component: api
14
+ template:
15
+ metadata:
16
+ labels:
17
+ {{- include "legion.selectorLabels" . | nindent 8 }}
18
+ app.kubernetes.io/component: api
19
+ spec:
20
+ serviceAccountName: {{ .Values.serviceAccount.name }}
21
+ containers:
22
+ - name: api
23
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
24
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
25
+ command: ["bundle", "exec", "legion", "api"]
26
+ ports:
27
+ - containerPort: {{ .Values.api.port }}
28
+ protocol: TCP
29
+ livenessProbe:
30
+ httpGet:
31
+ path: /api/health
32
+ port: {{ .Values.api.port }}
33
+ initialDelaySeconds: 10
34
+ periodSeconds: 30
35
+ readinessProbe:
36
+ httpGet:
37
+ path: /api/health
38
+ port: {{ .Values.api.port }}
39
+ initialDelaySeconds: 5
40
+ periodSeconds: 10
41
+ resources:
42
+ {{- toYaml .Values.api.resources | nindent 12 }}
43
+ env:
44
+ - name: LEGION_TRANSPORT_HOST
45
+ value: {{ .Values.rabbitmq.host | quote }}
46
+ - name: LEGION_DATA_URL
47
+ value: "postgres://$(DB_USER):$(DB_PASS)@{{ .Values.postgresql.host }}:{{ .Values.postgresql.port }}/{{ .Values.postgresql.database }}"
48
+ {{- with .Values.api.env }}
49
+ {{- toYaml . | nindent 12 }}
50
+ {{- end }}
51
+ envFrom:
52
+ - secretRef:
53
+ name: {{ .Values.rabbitmq.existingSecret }}
54
+ - secretRef:
55
+ name: {{ .Values.postgresql.existingSecret }}
@@ -0,0 +1,46 @@
1
+ apiVersion: apps/v1
2
+ kind: Deployment
3
+ metadata:
4
+ name: {{ include "legion.fullname" . }}-worker
5
+ labels:
6
+ {{- include "legion.labels" . | nindent 4 }}
7
+ app.kubernetes.io/component: worker
8
+ spec:
9
+ replicas: {{ .Values.worker.replicas }}
10
+ selector:
11
+ matchLabels:
12
+ {{- include "legion.selectorLabels" . | nindent 6 }}
13
+ app.kubernetes.io/component: worker
14
+ template:
15
+ metadata:
16
+ labels:
17
+ {{- include "legion.selectorLabels" . | nindent 8 }}
18
+ app.kubernetes.io/component: worker
19
+ spec:
20
+ serviceAccountName: {{ .Values.serviceAccount.name }}
21
+ containers:
22
+ - name: worker
23
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
24
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
25
+ command: ["bundle", "exec", "legion", "start"]
26
+ resources:
27
+ {{- toYaml .Values.worker.resources | nindent 12 }}
28
+ env:
29
+ - name: LEGION_TRANSPORT_HOST
30
+ value: {{ .Values.rabbitmq.host | quote }}
31
+ - name: LEGION_TRANSPORT_PORT
32
+ value: {{ .Values.rabbitmq.port | quote }}
33
+ - name: LEGION_DATA_URL
34
+ value: "postgres://$(DB_USER):$(DB_PASS)@{{ .Values.postgresql.host }}:{{ .Values.postgresql.port }}/{{ .Values.postgresql.database }}"
35
+ {{- with .Values.worker.env }}
36
+ {{- toYaml . | nindent 12 }}
37
+ {{- end }}
38
+ envFrom:
39
+ - secretRef:
40
+ name: {{ .Values.rabbitmq.existingSecret }}
41
+ - secretRef:
42
+ name: {{ .Values.postgresql.existingSecret }}
43
+ {{- with .Values.nodeSelector }}
44
+ nodeSelector:
45
+ {{- toYaml . | nindent 8 }}
46
+ {{- end }}
@@ -0,0 +1,22 @@
1
+ {{- if .Values.worker.hpa.enabled }}
2
+ apiVersion: autoscaling/v2
3
+ kind: HorizontalPodAutoscaler
4
+ metadata:
5
+ name: {{ include "legion.fullname" . }}-worker
6
+ labels:
7
+ {{- include "legion.labels" . | nindent 4 }}
8
+ spec:
9
+ scaleTargetRef:
10
+ apiVersion: apps/v1
11
+ kind: Deployment
12
+ name: {{ include "legion.fullname" . }}-worker
13
+ minReplicas: {{ .Values.worker.hpa.minReplicas }}
14
+ maxReplicas: {{ .Values.worker.hpa.maxReplicas }}
15
+ metrics:
16
+ - type: Resource
17
+ resource:
18
+ name: cpu
19
+ target:
20
+ type: Utilization
21
+ averageUtilization: {{ .Values.worker.hpa.targetCPUUtilization }}
22
+ {{- end }}
@@ -0,0 +1,13 @@
1
+ {{- if .Values.podDisruptionBudget.enabled }}
2
+ apiVersion: policy/v1
3
+ kind: PodDisruptionBudget
4
+ metadata:
5
+ name: {{ include "legion.fullname" . }}
6
+ labels:
7
+ {{- include "legion.labels" . | nindent 4 }}
8
+ spec:
9
+ minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
10
+ selector:
11
+ matchLabels:
12
+ {{- include "legion.selectorLabels" . | nindent 6 }}
13
+ {{- end }}
@@ -0,0 +1,15 @@
1
+ apiVersion: v1
2
+ kind: Service
3
+ metadata:
4
+ name: {{ include "legion.fullname" . }}-api
5
+ labels:
6
+ {{- include "legion.labels" . | nindent 4 }}
7
+ spec:
8
+ type: {{ .Values.api.service.type }}
9
+ ports:
10
+ - port: {{ .Values.api.port }}
11
+ targetPort: {{ .Values.api.port }}
12
+ protocol: TCP
13
+ selector:
14
+ {{- include "legion.selectorLabels" . | nindent 4 }}
15
+ app.kubernetes.io/component: api
@@ -0,0 +1,8 @@
1
+ {{- if .Values.serviceAccount.create }}
2
+ apiVersion: v1
3
+ kind: ServiceAccount
4
+ metadata:
5
+ name: {{ .Values.serviceAccount.name }}
6
+ labels:
7
+ {{- include "legion.labels" . | nindent 4 }}
8
+ {{- end }}
@@ -0,0 +1,69 @@
1
+ image:
2
+ repository: ghcr.io/legionio/legion
3
+ tag: latest
4
+ pullPolicy: IfNotPresent
5
+
6
+ worker:
7
+ replicas: 2
8
+ resources:
9
+ requests:
10
+ cpu: 250m
11
+ memory: 256Mi
12
+ limits:
13
+ cpu: 1000m
14
+ memory: 512Mi
15
+ hpa:
16
+ enabled: false
17
+ minReplicas: 2
18
+ maxReplicas: 20
19
+ targetCPUUtilization: 70
20
+ env: []
21
+
22
+ api:
23
+ replicas: 2
24
+ port: 4567
25
+ resources:
26
+ requests:
27
+ cpu: 100m
28
+ memory: 128Mi
29
+ limits:
30
+ cpu: 500m
31
+ memory: 256Mi
32
+ service:
33
+ type: ClusterIP
34
+ ingress:
35
+ enabled: false
36
+ env: []
37
+
38
+ rabbitmq:
39
+ host: rabbitmq
40
+ port: 5672
41
+ vhost: /
42
+ existingSecret: legion-rabbitmq
43
+
44
+ postgresql:
45
+ host: postgresql
46
+ port: 5432
47
+ database: legion
48
+ existingSecret: legion-postgresql
49
+
50
+ redis:
51
+ host: redis
52
+ port: 6379
53
+
54
+ vault:
55
+ enabled: false
56
+ address: ""
57
+ role: legion
58
+
59
+ serviceAccount:
60
+ create: true
61
+ name: legion
62
+
63
+ podDisruptionBudget:
64
+ enabled: true
65
+ minAvailable: 1
66
+
67
+ nodeSelector: {}
68
+ tolerations: []
69
+ affinity: {}
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ class API < Sinatra::Base
5
+ module Routes
6
+ module Governance
7
+ def self.registered(app)
8
+ app.helpers GovernanceHelpers
9
+ register_approvals(app)
10
+ end
11
+
12
+ module GovernanceHelpers
13
+ def run_governance_runner(method, **)
14
+ require 'legion/extensions/audit/runners/approval_queue'
15
+ runner = Object.new.extend(Legion::Extensions::Audit::Runners::ApprovalQueue)
16
+ runner.send(method, **)
17
+ rescue LoadError
18
+ halt 503, json_error('service_unavailable', 'lex-audit not available', status_code: 503)
19
+ end
20
+ end
21
+
22
+ def self.register_approvals(app)
23
+ app.get '/api/governance/approvals' do
24
+ require_data!
25
+ result = run_governance_runner(:list_pending,
26
+ tenant_id: params[:tenant_id],
27
+ limit: (params[:limit] || 50).to_i)
28
+ json_response(result)
29
+ end
30
+
31
+ app.get '/api/governance/approvals/:id' do
32
+ require_data!
33
+ result = run_governance_runner(:show_approval, id: params[:id].to_i)
34
+ if result[:success]
35
+ json_response(result)
36
+ else
37
+ halt 404, json_error('not_found', 'Approval not found', status_code: 404)
38
+ end
39
+ end
40
+
41
+ app.post '/api/governance/approvals' do
42
+ require_data!
43
+ body = parse_request_body
44
+ halt 422, json_error('missing_field', 'approval_type is required', status_code: 422) unless body[:approval_type]
45
+ halt 422, json_error('missing_field', 'requester_id is required', status_code: 422) unless body[:requester_id]
46
+
47
+ result = run_governance_runner(:submit,
48
+ approval_type: body[:approval_type],
49
+ payload: body[:payload] || {},
50
+ requester_id: body[:requester_id],
51
+ tenant_id: body[:tenant_id])
52
+ json_response(result, status_code: 201)
53
+ end
54
+
55
+ app.put '/api/governance/approvals/:id/approve' do
56
+ require_data!
57
+ body = parse_request_body
58
+ halt 422, json_error('missing_field', 'reviewer_id is required', status_code: 422) unless body[:reviewer_id]
59
+
60
+ result = run_governance_runner(:approve,
61
+ id: params[:id].to_i,
62
+ reviewer_id: body[:reviewer_id])
63
+ json_response(result)
64
+ end
65
+
66
+ app.put '/api/governance/approvals/:id/reject' do
67
+ require_data!
68
+ body = parse_request_body
69
+ halt 422, json_error('missing_field', 'reviewer_id is required', status_code: 422) unless body[:reviewer_id]
70
+
71
+ result = run_governance_runner(:reject,
72
+ id: params[:id].to_i,
73
+ reviewer_id: body[:reviewer_id])
74
+ json_response(result)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end