Repository navigation
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
1352 lines (1309 loc) · 66.2 KB
/
Copy pathdocker-compose.yml
File metadata and controls
1352 lines (1309 loc) · 66.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
x-orchestrator-env: &orchestrator-env
# Database (use container name, not localhost)
ROBOCO_DATABASE_HOST: roboco-postgres
ROBOCO_DATABASE_PORT: 5432
ROBOCO_DATABASE_USER: roboco
ROBOCO_DATABASE_PASSWORD: roboco
ROBOCO_DATABASE_NAME: roboco
# Redis (use container name)
ROBOCO_REDIS_HOST: roboco-redis
ROBOCO_REDIS_PORT: 6379
# API
ROBOCO_HOST: 0.0.0.0
ROBOCO_PORT: 8000
ROBOCO_ENCRYPTION_KEY: ${ROBOCO_ENCRYPTION_KEY:?ROBOCO_ENCRYPTION_KEY is required}
# HMAC secret for agent auth tokens. Orchestrator signs tokens
# per-agent at spawn; API middleware verifies them. Generate with:
# python -c 'import secrets; print(secrets.token_hex(32))'
ROBOCO_AGENT_AUTH_SECRET: ${ROBOCO_AGENT_AUTH_SECRET:?ROBOCO_AGENT_AUTH_SECRET is required}
# Set to "true" to require tokens on every API call (fail-closed).
# Leave unset/false during rollout so the panel + curl still work.
ROBOCO_AGENT_AUTH_REQUIRED: ${ROBOCO_AGENT_AUTH_REQUIRED:-false}
# Ollama (use container name)
ROBOCO_LOCAL_LLM_BASE_URL: http://roboco-ollama:11434/v1
ROBOCO_LOCAL_LLM_MODEL: glm-5.3:cloud
ROBOCO_DEFAULT_EMBEDDING_MODEL: qwen3-embedding:0.6b
ROBOCO_OLLAMA_BASE_URL: http://roboco-ollama:11434
# Video renderer sidecar (use container name). The video engine
# itself is default-off (video_engine_enabled); this just points the
# client at the sidecar for when it's armed.
ROBOCO_VIDEO_RENDERER_BASE_URL: http://roboco-video-renderer:3001
# Decisions service (typed System One decisions). Master flag armed ON for
# THIS (NAS/dev) deploy only - the registry compose ships it OFF: on the
# user-facing side nothing serves a verdict until the CEO arms it
# (Settings -> Feature Flags + Decisions Pilots). The Clef tier (self-hosted
# 27B, $0, roboco-decisions sidecar) is the sweet-spot default and the Laya
# tier (self-hosted 421M, also $0) the cheap fallback, both on-by-default
# once the master flag goes on; the OpenRouter fallback tier (Jev, the
# expensive option) stays OPT-IN (a deliberate spend).
# Pilot arming is a MIX per CEO decision 2026-09-23: cheap-failure /
# advisory / add-only pilots ON; anything with an expensive wrong answer
# (CEO-path routing, notification suppression, context dropping, lifecycle
# choices) SHADOW - verdicts logged against real outcomes, behavior
# unchanged. Per-pilot settings rows always win over these env defaults.
# self_heal's gate joined SHADOW (CEO decision 2026-09-26): the ENGINE is
# armed fully below, but the transient verdict is logged, not acted on,
# until a fine-tuned checkpoint clears the quality probe - shadow rows +
# the sweep's outcome labeler are exactly the labeled training data.
ROBOCO_DECISIONS_ENABLED: ${ROBOCO_DECISIONS_ENABLED:-true}
ROBOCO_DECISIONS_BASE_URL: ${ROBOCO_DECISIONS_BASE_URL:-http://roboco-decisions:8100}
ROBOCO_DECISIONS_TIER_CLEF_ENABLED: ${ROBOCO_DECISIONS_TIER_CLEF_ENABLED:-true}
ROBOCO_DECISIONS_TIER_LAYA_ENABLED: ${ROBOCO_DECISIONS_TIER_LAYA_ENABLED:-true}
ROBOCO_DECISIONS_TIER_OPENROUTER_ENABLED: ${ROBOCO_DECISIONS_TIER_OPENROUTER_ENABLED:-false}
ROBOCO_DECISIONS_PILOTS_ON: ${ROBOCO_DECISIONS_PILOTS_ON:-parking,preflight_diff,triage_failure,tool_spotlight,x_mention_triage,heal_severity,external_pr_triage,dep_update_risk,release_readiness,commit_intent,changelog_highlights,proactive_domain,idle_reaping,decision_note_sufficiency,park_cause,pre_send_completeness,findings_mapping,delta_brief,idle_legitimacy,review_queue_priority,branch_staleness,pm_closure_confidence,assembled_coherence,silent_exit}
ROBOCO_DECISIONS_PILOTS_SHADOW: ${ROBOCO_DECISIONS_PILOTS_SHADOW:-self_heal,complexity,steer_gate,transcript_notes,plan_quality,injection_screen,intake_preroute,collision_edge,ci_watch_route,release_worthy,second_review_eligibility,segment_classify,vault_prefilter,notify_dedup,board_due_early,stranded_response,coroner_gate,secretary_nl,tg_freetext_gate,memory_distill_gate,context_pruning,budget_wrapup,board_evidence_skip,respawn_verdict,submit_now_confidence,lesson_prune}
# Self-heal engine FULLY ARMED (CEO decision 2026-09-26): detect + notify +
# originate (tasks still held for the CEO's Approve-&-Start, per doctrine).
# Its decisions transient gate is what the decisions SHADOW list above
# logs, and the sweep's outcome labeler turns those logged verdicts into
# labeled fine-tuning rows against real CI outcomes.
# S3 object storage (RustFS) for rendered videos (use container name). Empty
# endpoint = disabled (FileResponse fallback); armed here for the NAS
# deploy, left OFF in docker-compose.registry.yml.
ROBOCO_MINIO_ENDPOINT: http://roboco-rustfs:9000
ROBOCO_MINIO_ACCESS_KEY: ${ROBOCO_MINIO_ACCESS_KEY:-minio}
ROBOCO_MINIO_SECRET_KEY: ${ROBOCO_MINIO_SECRET_KEY:-minio123}
ROBOCO_MINIO_BUCKET: ${ROBOCO_MINIO_BUCKET:-roboco-video-renders}
ROBOCO_MINIO_REGION: ${ROBOCO_MINIO_REGION:-us-east-1}
# Nebius Token Factory (opt-in): agents route to Nebius only after the key
# is saved and Nebius mode or pins are applied in the panel. Token Factory
# Sandboxes (dev/QA run_sandbox_tests in Nebius microVMs) stay OFF unless
# set true; the project id is the console's aiproject- id. Defaults mirror
# roboco/config.py.
ROBOCO_NEBIUS_BASE_URL: ${ROBOCO_NEBIUS_BASE_URL:-https://api.tokenfactory.nebius.com/v1}
ROBOCO_NEBIUS_CLI_MODEL: ${ROBOCO_NEBIUS_CLI_MODEL:-nvidia/nemotron-3-super-120b-a12b}
ROBOCO_NEBIUS_RATE_LIMIT_RETRY_AFTER_SECONDS: ${ROBOCO_NEBIUS_RATE_LIMIT_RETRY_AFTER_SECONDS:-60}
ROBOCO_NEBIUS_AUTH_RETRY_AFTER_SECONDS: ${ROBOCO_NEBIUS_AUTH_RETRY_AFTER_SECONDS:-60}
ROBOCO_TOKEN_FACTORY_SANDBOXES_ENABLED: ${ROBOCO_TOKEN_FACTORY_SANDBOXES_ENABLED:-false}
ROBOCO_TOKEN_FACTORY_SANDBOXES_BASE_URL: ${ROBOCO_TOKEN_FACTORY_SANDBOXES_BASE_URL:-https://api.tokenfactory.nebius.com/sandboxes}
ROBOCO_TOKEN_FACTORY_SANDBOXES_PROJECT_ID: ${ROBOCO_TOKEN_FACTORY_SANDBOXES_PROJECT_ID:-}
ROBOCO_TOKEN_FACTORY_SANDBOXES_IMAGE: ${ROBOCO_TOKEN_FACTORY_SANDBOXES_IMAGE:-tag:python:3.13}
ROBOCO_TOKEN_FACTORY_SANDBOXES_TIMEOUT_SECONDS: ${ROBOCO_TOKEN_FACTORY_SANDBOXES_TIMEOUT_SECONDS:-900}
ROBOCO_TOKEN_FACTORY_SANDBOXES_MAX_ARCHIVE_BYTES: ${ROBOCO_TOKEN_FACTORY_SANDBOXES_MAX_ARCHIVE_BYTES:-67108864}
# Host paths for spawning agent containers (required for Docker-in-Docker)
# IMPORTANT: These must be ABSOLUTE paths on the host filesystem
ROBOCO_HOST_PROJECT_DIR: ${ROBOCO_HOST_PROJECT_DIR:-/volume1/roboco}
ROBOCO_HOST_CLAUDE_DIR: ${ROBOCO_HOST_CLAUDE_DIR:-/home/renzof/.claude}
# SuperGrok auth (host ~/.grok) for Grok-CLI agents — the orchestrator
# mounts <dir>/auth.json into each Grok agent. Run `grok login` on the host.
ROBOCO_HOST_GROK_DIR: ${ROBOCO_HOST_GROK_DIR:-/home/renzof/.grok}
# ChatGPT-subscription auth (host ~/.codex) for Codex-CLI agents — same
# shape as ROBOCO_HOST_GROK_DIR. Run `codex login` on the host.
ROBOCO_HOST_CODEX_DIR: ${ROBOCO_HOST_CODEX_DIR:-/home/renzof/.codex}
# OAuth login (host ~/.gemini) for Gemini-CLI agents — the orchestrator
# mounts <dir>/oauth_creds.json (read-only) into each Gemini agent. Run
# `gemini` interactively once on the host to produce it.
ROBOCO_HOST_GEMINI_DIR: ${ROBOCO_HOST_GEMINI_DIR:-/home/renzof/.gemini}
# Kimi subscription auth (host ~/.kimi-code) for Kimi-CLI agents — the
# orchestrator mounts <dir>/credentials/kimi-code.json into each Kimi
# agent. Run `kimi login` on the host. Read-WRITE (see the volumes
# mount below): unlike gemini's reusable refresh token, Kimi's refresh
# is rotation-with-short-reuse-grace, so every container shares this
# ONE host chain via a symlinked-in credentials/+oauth/ mount rather
# than a per-container copy (kimi follows codex's RW mount mode here).
ROBOCO_HOST_KIMI_DIR: ${ROBOCO_HOST_KIMI_DIR:-/home/renzof/.kimi-code}
ROBOCO_KIMI_CLI_MODEL: ${ROBOCO_KIMI_CLI_MODEL:-kimi-code/k3}
ROBOCO_KIMI_RATE_LIMIT_RETRY_AFTER_SECONDS: ${ROBOCO_KIMI_RATE_LIMIT_RETRY_AFTER_SECONDS:-60}
ROBOCO_KIMI_AUTH_RETRY_AFTER_SECONDS: ${ROBOCO_KIMI_AUTH_RETRY_AFTER_SECONDS:-60}
ROBOCO_KIMI_MAX_CONCURRENT: ${ROBOCO_KIMI_MAX_CONCURRENT:-1}
ROBOCO_HOST_DATA_DIR: ${ROBOCO_HOST_DATA_DIR:-/volume1/roboco/data}
# Public base URL for commit-trailer links. Default 127.0.0.1 produces
# unusable links in commit message bodies; set to NAS LAN IP so
# f"{api_base}/tasks/{task_id}" renders a reachable URL.
ROBOCO_PUBLIC_BASE_URL: "http://192.168.50.111:8000"
# Production environment selects structlog's JSONRenderer (machine-
# parseable logs) over the dev ConsoleRenderer.
ROBOCO_ENVIRONMENT: production
# Reaper timeouts raised to 30 min so a long agent task isn't reaped
# mid-work. ROBOCO_CLAIM_STALE_SECONDS is the heartbeat-staleness window
# _reap_stale_claims uses (the one that was killing in-progress work).
ROBOCO_CLAIM_STALE_SECONDS: "1800"
ROBOCO_STALE_CLAIM_REAP_SECONDS: "1800"
# External-PR review. When on, the org discovers inbound external/fork PRs
# and the PR reviewer posts one change-request — READ-ONLY (it never runs
# contributor code). The supersede that fetches + builds their code is
# separately CEO-triggered and stays gated by require_human_confirm.
# Override either via .env. (Config default is off; this enables it here.)
ROBOCO_EXTERNAL_PR_ENABLED: ${ROBOCO_EXTERNAL_PR_ENABLED:-true}
ROBOCO_EXTERNAL_PR_REQUIRE_HUMAN_CONFIRM: ${ROBOCO_EXTERNAL_PR_REQUIRE_HUMAN_CONFIRM:-true}
# ROBOCO_EXTERNAL_PR_POLL_INTERVAL_SECONDS: "300"
# ROBOCO_EXTERNAL_PR_AUTHOR_ALLOWLIST: '["corey"]' # empty = every external PR
# Production self-healing ("engine 4"). RoboCo watches its OWN repo CI and,
# when red, notifies the CEO and (with originate on) opens a PENDING fix
# task that STOPS for the CEO's Approve-&-Start — it never self-deploys.
# FULLY ARMED here (both toggles on; CEO decision 2026-09-26, training-data
# rollout): the sweep's outcome labeler grades the logged transient
# verdicts against real CI outcomes, feeding the fine-tuning corpus (spec
# section 12). Set PROJECT_SLUG to the registered project that IS RoboCo;
# CI_WORKFLOW scopes the signal to the real CI workflow (RoboCo has several
# workflows, so the unscoped "latest run" would be unreliable).
ROBOCO_SELF_HEAL_ENABLED: ${ROBOCO_SELF_HEAL_ENABLED:-true}
ROBOCO_SELF_HEAL_ORIGINATE_ENABLED: ${ROBOCO_SELF_HEAL_ORIGINATE_ENABLED:-true}
ROBOCO_SELF_HEAL_PROJECT_SLUG: ${ROBOCO_SELF_HEAL_PROJECT_SLUG:-roboco-api}
ROBOCO_SELF_HEAL_CI_WORKFLOW: ${ROBOCO_SELF_HEAL_CI_WORKFLOW:-ci.yml}
# Agent runtime toolchain matching: provision each agent workspace with
# the TARGET project's Python (uv resolves requires-python) and block a
# delivery gate when the suite can't be executed instead of passing on a
# source read. Config default is OFF; enabled HERE (personal deploy) but
# deliberately left OFF in docker-compose.registry.yml so the published
# default stays conservative until it's verified on a live run.
ROBOCO_TOOLCHAIN_MATCH_ENABLED: ${ROBOCO_TOOLCHAIN_MATCH_ENABLED:-true}
# Architectural conventions standard: gate where code lives (placement,
# hygiene, modularity) via each project's .roboco/conventions.yml. Config
# default is OFF; enabled HERE (personal deploy), deliberately left OFF in
# docker-compose.registry.yml so the published default stays conservative.
ROBOCO_CONVENTIONS_ENABLED: ${ROBOCO_CONVENTIONS_ENABLED:-true}
# Autonomy engines shipped in 0.12.0 / 0.13.0 — all default-OFF in config;
# ARMED here for the NAS deploy (our live test bed). Each is bounded +
# CEO-gated by construction and overridable via .env. Left OFF in
# docker-compose.registry.yml so the published default stays conservative.
# - CI-watch: opens a fix task when a watched project's default-branch CI
# goes red — only for projects with the per-project ci_watch_enabled
# column set; never auto-merges (rides the PR-review gate).
# - Dep-update bot: weekly lockfile-diff probe -> "update dependencies"
# task — only for projects with a dep_update_command set; never merges.
# - Release manager: deterministic readiness sweep -> ONE release proposal
# HELD for the CEO; the executor publishes only on CEO approval + green
# CI (reuses SELF_HEAL_PROJECT_SLUG as the RoboCo repo).
# - Org-memory loop: distils a completion lesson + auto-injects relevant
# past lessons/playbooks into each claim's briefing (local model only).
# - X engine: drafts release-announcement + mention-reply posts, ALL
# held for per-post CEO approval in the panel; inert without stored
# X credentials (Settings -> the X card) regardless of this flag.
ROBOCO_CI_WATCH_ENABLED: ${ROBOCO_CI_WATCH_ENABLED:-true}
ROBOCO_DEP_UPDATE_ENABLED: ${ROBOCO_DEP_UPDATE_ENABLED:-true}
ROBOCO_DOCS_SYNC_ENABLED: ${ROBOCO_DOCS_SYNC_ENABLED:-true}
ROBOCO_RELEASE_MANAGER_ENABLED: ${ROBOCO_RELEASE_MANAGER_ENABLED:-true}
ROBOCO_ORG_MEMORY_ENABLED: ${ROBOCO_ORG_MEMORY_ENABLED:-true}
ROBOCO_X_ENGINE_ENABLED: ${ROBOCO_X_ENGINE_ENABLED:-true}
# Telegram notifications bridge: best-effort DMs to the CEO on escalation
# + completion. Armed by default here per the NAS-compose convention
# (new flags ship ON unless the CEO opts out); inert without stored
# bot-token + chat-id credentials regardless of this flag.
ROBOCO_TELEGRAM_ENABLED: ${ROBOCO_TELEGRAM_ENABLED:-true}
# Telegram V2 — inbound commands + actionable approve/reject buttons.
# Armed here (sub-switch of ROBOCO_TELEGRAM_ENABLED above); the whole
# bridge stays inert until that flag AND credentials
# are both set, so arming this alone does nothing yet.
ROBOCO_TELEGRAM_INBOUND_ENABLED: ${ROBOCO_TELEGRAM_INBOUND_ENABLED:-true}
# Telegram Mini App sign-in: validates Telegram's signed WebApp initData
# and mints the same cloud-auth session cookie /api/auth/login issues,
# so the CEO's phone becomes an authenticated panel client. Requires
# ROBOCO_CLOUD_AUTH_ENABLED=true (startup fails loud otherwise) AND a
# public HTTPS origin (the cookie is secure-only, and Telegram itself
# only opens Mini Apps over https). Armed by default per the NAS-compose
# convention — NOTE: boot fails loud if cloud auth is disabled/unseeded,
# so a cloud-auth-off .env must also set this false.
ROBOCO_TELEGRAM_MINIAPP_ENABLED: ${ROBOCO_TELEGRAM_MINIAPP_ENABLED:-true}
ROBOCO_OBSIDIAN_VAULT_ENABLED: ${ROBOCO_OBSIDIAN_VAULT_ENABLED:-true}
ROBOCO_VAULT_PATH: ${ROBOCO_VAULT_PATH:-/app/vault}
ROBOCO_VAULT_INTAKE_ENABLED: ${ROBOCO_VAULT_INTAKE_ENABLED:-true}
ROBOCO_VAULT_ARCHIVE_DAYS: ${ROBOCO_VAULT_ARCHIVE_DAYS:-30}
ROBOCO_VAULT_REPORT_ENABLED: ${ROBOCO_VAULT_REPORT_ENABLED:-true}
ROBOCO_VAULT_KB_ENABLED: ${ROBOCO_VAULT_KB_ENABLED:-true}
ROBOCO_VAULT_KB_DIRS: ${ROBOCO_VAULT_KB_DIRS:-RoboCo/Notes}
ROBOCO_VAULT_KB_INTERVAL_SECONDS: ${ROBOCO_VAULT_KB_INTERVAL_SECONDS:-3600}
# - Board roadmap engine: weekly, opens ONE held exploration task for
# the Product Owner, who proposes a themed cycle of roadmap items;
# the CEO approves each item individually into the backlog.
ROBOCO_ROADMAP_ENGINE_ENABLED: ${ROBOCO_ROADMAP_ENGINE_ENABLED:-true}
# Video-generation engine: a UX/UI dev authors a bespoke HyperFrames video
# per release/spotlight/on-demand trigger through the normal delivery
# lifecycle; the render loop above renders it via the video-renderer
# sidecar and holds the clip as a CEO-approval draft — nothing
# auto-posts. Config default is OFF; ARMED here for the NAS deploy like
# the rest. Left OFF in docker-compose.registry.yml so the published
# default stays conservative until verified on a live run.
ROBOCO_VIDEO_ENGINE_ENABLED: ${ROBOCO_VIDEO_ENGINE_ENABLED:-true}
ROBOCO_VIDEO_ON_RELEASE: ${ROBOCO_VIDEO_ON_RELEASE:-true}
ROBOCO_VIDEO_ON_SPOTLIGHT: ${ROBOCO_VIDEO_ON_SPOTLIGHT:-true}
# Fable-mode: composes the Fable behavioral doctrine into every agent's
# system prompt + installs turn-discipline/honesty/verification hooks
# at spawn (both runtimes). Config default is OFF; ARMED here for the
# NAS deploy (our live test bed) like the rest. Left OFF in
# docker-compose.registry.yml so the published default stays
# conservative until verified on a live run.
ROBOCO_FABLE_MODE_ENABLED: ${ROBOCO_FABLE_MODE_ENABLED:-true}
# Sandboxed per-agent test DB/Redis: orchestrator-provisioned throwaway
# sibling containers per spawn, replacing the prod-creds gate-env
# injection for an opted-in project (its `sandbox_services` column set).
# Config default is OFF; enabled HERE (personal deploy), deliberately
# left OFF in docker-compose.registry.yml so the published default
# stays conservative until verified on a live run.
ROBOCO_SANDBOX_DB_ENABLED: ${ROBOCO_SANDBOX_DB_ENABLED:-true}
# DB network isolation is LIVE in this file (postgres/redis on the
# data-only network): suppress the legacy prod-creds gate-env
# injection — agents can't reach roboco-postgres anyway, and handing
# out creds that dead-end in a connect timeout is worse than none.
# This flag must always travel with the networks: topology above.
ROBOCO_DB_NETWORK_ISOLATED: ${ROBOCO_DB_NETWORK_ISOLATED:-true}
# Strategy engine (proactive strategy signals) + internal PR review — both
# default-OFF in config; ARMED here for the NAS deploy like the rest. Left
# OFF in docker-compose.registry.yml so the published default stays conservative.
ROBOCO_STRATEGY_ENGINE_ENABLED: ${ROBOCO_STRATEGY_ENGINE_ENABLED:-true}
ROBOCO_INTERNAL_PR_ENABLED: ${ROBOCO_INTERNAL_PR_ENABLED:-true}
# Remaining default-OFF capabilities, ARMED here (personal deploy): web
# research (live by default via the bundled searxng container; the
# three lines below are what actually gets the .env key/provider/URL into
# the container; without them .env values never reach it), pitch
# auto-provisioning (inert until a pitch is approved + a git token is
# set — same deal for ROBOCO_PROVISIONING_TOKEN/_ORG below), and
# transcript pruning (retention maintenance). All left OFF in the
# registry compose. Override any via .env.
ROBOCO_RESEARCH_ENABLED: ${ROBOCO_RESEARCH_ENABLED:-true}
ROBOCO_RESEARCH_API_KEY: ${ROBOCO_RESEARCH_API_KEY:-}
ROBOCO_RESEARCH_PROVIDER: ${ROBOCO_RESEARCH_PROVIDER:-searxng}
ROBOCO_RESEARCH_SEARXNG_BASE_URL: ${ROBOCO_RESEARCH_SEARXNG_BASE_URL:-http://roboco-searxng:8080}
ROBOCO_PROVISIONING_ENABLED: ${ROBOCO_PROVISIONING_ENABLED:-true}
ROBOCO_PROVISIONING_TOKEN: ${ROBOCO_PROVISIONING_TOKEN:-}
ROBOCO_PROVISIONING_ORG: ${ROBOCO_PROVISIONING_ORG:-}
ROBOCO_TRANSCRIPT_PRUNE_ENABLED: ${ROBOCO_TRANSCRIPT_PRUNE_ENABLED:-true}
# Two knobs that also default ON here (like every feature), but change
# FAILURE / AUTH behavior — the .env must be set up for each before a real
# boot, which the operator owns:
# - CLOUD_AUTH: exposes the panel behind a login. Set
# ROBOCO_CLOUD_AUTH_EMAIL / _PASSWORD / _SECRET in .env (startup fails
# loud without the secret) and terminate TLS (the session cookie is
# secure-only); leave ROBOCO_PANEL_AGENT_TOKEN unset (it bypasses
# login). To test-deploy before the creds exist, set
# ROBOCO_CLOUD_AUTH_ENABLED=false in .env for that run.
# - ROUTING_STRICT: fail-closed model routing — a spawn whose provider is
# disabled RAISES instead of degrading. Audit model_assignments first
# (a stale row pointing at a disabled provider would crash that spawn).
ROBOCO_CLOUD_AUTH_ENABLED: ${ROBOCO_CLOUD_AUTH_ENABLED:-true}
# .env values only reach the container when referenced here — without
# these lines the seeded login/secret never arrive and startup fails
# loud (ENABLED defaults true above).
ROBOCO_CLOUD_AUTH_EMAIL: ${ROBOCO_CLOUD_AUTH_EMAIL:-}
ROBOCO_CLOUD_AUTH_PASSWORD: ${ROBOCO_CLOUD_AUTH_PASSWORD:-}
ROBOCO_CLOUD_AUTH_SECRET: ${ROBOCO_CLOUD_AUTH_SECRET:-}
ROBOCO_CLOUD_AUTH_COOKIE_MAX_AGE: ${ROBOCO_CLOUD_AUTH_COOKIE_MAX_AGE:-2592000}
ROBOCO_ROUTING_STRICT: ${ROBOCO_ROUTING_STRICT:-true}
# Spawn preflight (token-opt Phase 3) — refuse a non-gateway delivery role
# that would respawn forever. Default-OFF in config; ARMED here (inert in
# practice: every real delivery role is gateway-enabled). OFF in registry.
ROBOCO_SPAWN_PREFLIGHT_ENABLED: ${ROBOCO_SPAWN_PREFLIGHT_ENABLED:-true}
# fastapi-guard HTTP security layer (v0.16.0). ACTIVE enforcement: passive
# / log-only calibration (WAF false-positive review, see
# docs/rag/architecture/http-security-guard.md) came back clean, CEO
# approved flipping to active now that cloud auth + Tailscale are armed.
# Guard mounts, observes, AND BLOCKS matching requests. FAIL_SECURE=true
# (fail-secure ON): a guard-internal check error blocks the request
# rather than letting it through. Left OFF entirely in the registry
# compose.
# enforce_https follows ROBOCO_ENVIRONMENT (dev on the NAS → not enforced).
ROBOCO_GUARD_ENABLED: ${ROBOCO_GUARD_ENABLED:-true}
ROBOCO_GUARD_PASSIVE_MODE: ${ROBOCO_GUARD_PASSIVE_MODE:-false}
ROBOCO_GUARD_FAIL_SECURE: ${ROBOCO_GUARD_FAIL_SECURE:-true}
# Emergency-lockdown whitelist escape hatch. Without this line here,
# setting it in .env silently does nothing — only vars listed in this
# stanza reach the container.
ROBOCO_GUARD_EMERGENCY_WHITELIST: ${ROBOCO_GUARD_EMERGENCY_WHITELIST:-}
# Trusted local-proxy hop IPs (docker bridge gateway) for the XFF
# real-client resolver — set to the bridge gateway when Tailscale Serve
# is gateway-fronted, else the guard sees a whitelisted hop and the WAF
# goes inert for /tg. Same "must be listed here to reach the container"
# rule as the emergency whitelist above.
ROBOCO_GUARD_TRUSTED_CHAIN_PEERS: ${ROBOCO_GUARD_TRUSTED_CHAIN_PEERS:-}
# Log level for guard-core's suspicious-path lines (IP bans, blocked
# countries, BehaviorTracker). Same "must be listed here" rule as above.
ROBOCO_GUARD_LOG_SUSPICIOUS_LEVEL: ${ROBOCO_GUARD_LOG_SUSPICIOUS_LEVEL:-WARNING}
# guard-agent telemetry to the guard-core SaaS. OFF: CEO opt-out
# 2026-09-02 (guard-agent is not used and not a dependency). Carried so
# it stays armable via .env; with no key roboco leaves the agent
# unconfigured and nothing leaves the box. The cache path keeps the
# last-known dynamic rules across a restart during a SaaS outage
# (guard-core 3.17.0); /data/logs is the persisted logs mount.
ROBOCO_GUARD_TELEMETRY_ENABLED: ${ROBOCO_GUARD_TELEMETRY_ENABLED:-false}
ROBOCO_GUARD_AGENT_API_KEY: ${ROBOCO_GUARD_AGENT_API_KEY:-}
ROBOCO_GUARD_PROJECT_ID: ${ROBOCO_GUARD_PROJECT_ID:-}
ROBOCO_GUARD_DYNAMIC_RULES_CACHE_PATH: ${ROBOCO_GUARD_DYNAMIC_RULES_CACHE_PATH:-/data/logs/guard-dynamic-rules.json}
ROBOCO_AGENT_TOOL_CALL_HALT: ${ROBOCO_AGENT_TOOL_CALL_HALT:-600}
ROBOCO_AGENT_TOOL_CALL_WARN: ${ROBOCO_AGENT_TOOL_CALL_WARN:-200}
# Per-task/project cost budgets (default-off subsystem; also on the
# panel feature-flags card).
ROBOCO_TASK_BUDGETS_ENABLED: ${ROBOCO_TASK_BUDGETS_ENABLED:-true}
x-orchestrator-volumes: &orchestrator-volumes
# Docker socket - allows spawning agent containers
- /var/run/docker.sock:/var/run/docker.sock
# Claude Code auth - mount your ~/.claude directory
- ${CLAUDE_AUTH_DIR:-/home/renzof/.claude}:/root/.claude
# SuperGrok auth — mount the host ~/.grok at the SAME host path the
# orchestrator passes to each Grok agent's `-v`, so its auth.json exists()
# check passes here AND the agent bind resolves on the host. One canonical
# var for source AND target (they must be equal in docker-in-docker).
# Read-WRITE: the orchestrator auto-refreshes the ~6h token in place
# (grok_auth.refresh_if_stale) so agents never mount a dead credential;
# each agent's own auth.json mount stays read-only.
- ${ROBOCO_HOST_GROK_DIR:-/home/renzof/.grok}:${ROBOCO_HOST_GROK_DIR:-/home/renzof/.grok}
# Codex CLI auth — same shape as the SuperGrok mount above. Read-WRITE:
# the orchestrator auto-refreshes the access token in place
# (codex_auth.refresh_if_stale); each agent's own mount stays read-only.
- ${ROBOCO_HOST_CODEX_DIR:-/home/renzof/.codex}:${ROBOCO_HOST_CODEX_DIR:-/home/renzof/.codex}
# OAuth login for Gemini-CLI agents — mount the host ~/.gemini at the
# SAME host path the orchestrator passes to each Gemini agent's `-v`, so
# its oauth_creds.json exists() check passes here AND the agent bind
# resolves on the host. Read-ONLY: unlike grok's single-use SuperGrok
# token, Google's OAuth refresh token is reusable and refreshed
# IN-PROCESS by each agent container's own CLI — never by the
# orchestrator — so no read-write access is needed here.
- ${ROBOCO_HOST_GEMINI_DIR:-/home/renzof/.gemini}:${ROBOCO_HOST_GEMINI_DIR:-/home/renzof/.gemini}:ro
# Kimi subscription auth — mount the host ~/.kimi-code at the SAME host
# path the orchestrator passes to each Kimi agent's `-v`, so the
# credentials/kimi-code.json exists() check passes here AND the agent
# bind resolves on the host. Read-WRITE (unlike gemini's RO mount
# above): Moonshot's refresh token is rotation-with-short-reuse-grace,
# not truly reusable, so every container symlinks credentials/+oauth/
# from this ONE shared chain instead of refreshing a private copy —
# the CLI's own cross-process lock (oauth/kimi-code.lock) serializes
# redemptions. No orchestrator refresh daemon (the CLI refreshes
# itself); this mount just needs to be writable so the CLI can.
- ${ROBOCO_HOST_KIMI_DIR:-/home/renzof/.kimi-code}:${ROBOCO_HOST_KIMI_DIR:-/home/renzof/.kimi-code}
# Shared config directory for MCP configs (writable)
- ${ROBOCO_DATA_DIR:-./data}/mcp-configs:/app/mcp-configs
- ${ROBOCO_DATA_DIR:-./data}/vault:/app/vault
# Generated prompts directory - composed at runtime from layers
- ${ROBOCO_DATA_DIR:-./data}/prompts-generated:/app/prompts-generated
# Per-agent Claude settings (generated at spawn time)
- ${ROBOCO_DATA_DIR:-./data}/agent-settings:/app/agent-settings
# Agent workspaces (git clones) - persisted across restarts
- ${ROBOCO_DATA_DIR:-./data}/workspaces:/data/workspaces
# Per-agent GROK usage capture: each Grok agent writes usage.json under
# <agent_id>/; the finalizer reads the captured tokens/cost back here.
- ${ROBOCO_DATA_DIR:-./data}/grok-usage:/data/grok-usage
# Per-agent CODEX usage capture — same shape as grok-usage above.
- ${ROBOCO_DATA_DIR:-./data}/codex-usage:/data/codex-usage
# Per-agent GEMINI usage capture: each Gemini agent writes usage.json
# under <agent_id>/; the finalizer reads the captured tokens/cost back here.
- ${ROBOCO_DATA_DIR:-./data}/gemini-usage:/data/gemini-usage
# Per-agent KIMI usage capture — same shape as grok/codex/gemini-usage.
- ${ROBOCO_DATA_DIR:-./data}/kimi-usage:/data/kimi-usage
# Per-agent HUMMIN usage capture — same shape as grok/codex/gemini/kimi-usage.
- ${ROBOCO_DATA_DIR:-./data}/hummin-usage:/data/hummin-usage
# Persistent logs — survive `docker compose down/up`. Orchestrator and
# each spawned agent write structured logs here so we can audit past
# runs instead of relying on ephemeral `docker logs`.
- ${ROBOCO_DATA_DIR:-./data}/logs:/data/logs
# Rendered MP4s from the video engine — persisted so renders survive
# container recreation (default ROBOCO_VIDEO_OUTPUT_DIR=/data/video-renders).
- ${ROBOCO_DATA_DIR:-./data}/video-renders:/data/video-renders
# Per-agent SessionStart briefings (pre-rendered task context)
- ${ROBOCO_DATA_DIR:-./data}/briefings:/app/briefings
# Per-agent spawn manifests (role-scoped tool list) — written by the
# orchestrator to /app/manifests, bind-mounted into each agent container
# as /app/tool-manifest.json. Without this mount the file is written to
# the orchestrator's ephemeral fs, never reaches the host, and agents
# fall back to all-verbs registration.
- ${ROBOCO_DATA_DIR:-./data}/manifests:/app/manifests
services:
# ==========================================================================
# PostgreSQL - Primary Database with pgvector for RAG
# ==========================================================================
postgres:
image: pgvector/pgvector:pg16
container_name: roboco-postgres
restart: unless-stopped
# pg_stat_statements only collects anything once shared_preload_libraries
# names it at server start (alembic migration 095 just CREATE EXTENSIONs
# it, which is a no-op for stats without this). Standard postgres image
# ENTRYPOINT is docker-entrypoint.sh; overriding CMD here still runs the
# normal init/initdb path, it just execs postgres with this extra flag, and
# the container restarts once on the next bring-up to pick up the GUC.
command: ["postgres", "-c", "shared_preload_libraries=pg_stat_statements"]
# data-only network: agent containers (on roboco_default) cannot reach
# the production DB; only the multi-homed orchestrator can. Host port
# publishing (15432) is unaffected — roboco_data is a normal bridge.
networks:
- data
environment:
POSTGRES_USER: roboco
POSTGRES_PASSWORD: roboco
POSTGRES_DB: roboco
ports:
- "15432:5432"
volumes:
- ${ROBOCO_DATA_DIR:-./data}/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U roboco -d roboco"]
interval: 10s
timeout: 5s
retries: 5
# ==========================================================================
# Redis - Cache, Sessions, Event Bus
# ==========================================================================
redis:
image: redis:8-alpine
container_name: roboco-redis
restart: unless-stopped
# data-only network — see postgres. Redis has no auth, so network
# membership is its ONLY containment against agent containers.
networks:
- data
command: redis-server --appendonly yes
ports:
- "16379:6379"
volumes:
- ${ROBOCO_DATA_DIR:-./data}/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# ==========================================================================
# Backup - periodic pg_dump of the roboco DB (interim; no PITR/WAL yet)
# ==========================================================================
backup:
image: pgvector/pgvector:pg16
container_name: roboco-backup
restart: unless-stopped
# data-only network — pg_dump reaches postgres by container name, same
# as every other data-network consumer; never exposed to the agent mesh.
networks:
- data
environment:
POSTGRES_HOST: roboco-postgres
POSTGRES_PORT: 5432
POSTGRES_USER: roboco
POSTGRES_PASSWORD: roboco
POSTGRES_DB: roboco
# Armed only when ROBOCO_BACKUP_MIRROR_DIR is set in .env; that host
# path must live on a DIFFERENT disk (external/remote mount) — a
# same-disk mirror protects nothing. Unset → the script skips mirroring.
BACKUP_MIRROR_DIR: ${ROBOCO_BACKUP_MIRROR_DIR:+/backups-mirror}
entrypoint: ["/bin/bash", "/scripts/backup-entrypoint.sh"]
volumes:
- ./docker/scripts/entrypoints/backup-entrypoint.sh:/scripts/backup-entrypoint.sh:ro
- ${ROBOCO_DATA_DIR:-./data}/backups:/backups
# Unarmed default deliberately re-mounts the primary backups dir (it
# already exists, so no stray root-owned dir is auto-created) — the
# script never writes there while BACKUP_MIRROR_DIR is empty.
- ${ROBOCO_BACKUP_MIRROR_DIR:-${ROBOCO_DATA_DIR:-./data}/backups}:/backups-mirror
depends_on:
postgres:
condition: service_healthy
# ==========================================================================
# RustFS - S3 object storage for rendered videos (NAS default-on; registry OFF)
# ==========================================================================
# Replaces MinIO, whose images are gone from docker.io (404) and quay.io.
# RustFS (Apache-2.0) speaks the same S3 API, so roboco's minio-py client
# and the ROBOCO_MINIO_* settings work unchanged. 1.0.1 multi-arch digest.
# Own volume: the image runs as uid 10001 and cannot write MinIO's
# root-owned data dir.
rustfs:
image: rustfs/rustfs@sha256:1803faef57627e2d9c2e7d89d655d712ddded5389040054987163043fecb6a3c
container_name: roboco-rustfs
restart: unless-stopped
# data-only network: keeps object storage off the agent mesh; the
# multi-homed orchestrator reaches it via its data NIC. Host ports for
# debugging.
networks:
- data
environment:
RUSTFS_ACCESS_KEY: ${ROBOCO_MINIO_ACCESS_KEY:-minio}
RUSTFS_SECRET_KEY: ${ROBOCO_MINIO_SECRET_KEY:-minio123}
RUSTFS_ADDRESS: 0.0.0.0:9000
RUSTFS_CONSOLE_ADDRESS: 0.0.0.0:9001
RUSTFS_CONSOLE_ENABLE: "true"
ports:
- "19000:9000"
- "19001:9001"
volumes:
- rustfs-data:/data
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:9000/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
# Bucket init - one-shot, mirrors the ollama-init pattern: depends on
# rustfs healthy, creates the bucket idempotently, then exits. `|| true`
# covers re-runs (bucket already exists). aws-cli 2.38.1 multi-arch digest.
rustfs-init:
image: amazon/aws-cli@sha256:733d5be1015825e4038c6016f3fa9954868ddee1f611b73be795a9cc2e706bfd
container_name: roboco-rustfs-init
depends_on:
rustfs:
condition: service_healthy
networks:
- data
entrypoint: ["/bin/sh", "-c"]
# Note: $$ escapes $ for docker-compose variable substitution so the
# container's own env vars reach aws.
command:
- aws --endpoint-url http://roboco-rustfs:9000 s3 mb s3://$$ROBOCO_MINIO_BUCKET || true
environment:
AWS_ACCESS_KEY_ID: ${ROBOCO_MINIO_ACCESS_KEY:-minio}
AWS_SECRET_ACCESS_KEY: ${ROBOCO_MINIO_SECRET_KEY:-minio123}
AWS_DEFAULT_REGION: ${ROBOCO_MINIO_REGION:-us-east-1}
ROBOCO_MINIO_BUCKET: ${ROBOCO_MINIO_BUCKET:-roboco-video-renders}
restart: "no"
# ==========================================================================
# Ollama - Local LLM and Embedding Server
# ==========================================================================
ollama:
image: ollama/ollama:latest
container_name: roboco-ollama
restart: unless-stopped
environment:
OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
ports:
- "11435:11434"
volumes:
- ${ROBOCO_DATA_DIR:-./data}/ollama:/root/.ollama
healthcheck:
# Use ollama CLI (guaranteed available) to check if server is responding
test: ["CMD", "ollama", "list"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
# Ollama model puller - pulls required models on startup
# Uses streaming curl to wait for full model download
ollama-init:
image: curlimages/curl:latest
container_name: roboco-ollama-init
depends_on:
ollama:
condition: service_healthy
restart: "no"
entrypoint: ["/bin/sh", "-c"]
command:
- |
# NO `set -e`: pulls are best-effort. Cached models persist in the ollama
# volume and MUST survive a slow/unreachable ollama registry — a manifest
# re-check failure there must never down a fully-cached deployment (it
# used to: a degraded registry made `ollama pull` fail under set -e, which
# blocked the orchestrator's service_completed_successfully gate). Success
# is gated on the models being PRESENT (verify step), not on the pull.
# Note: $$ escapes $ for docker-compose variable substitution.
echo "=== Pulling embedding model (qwen3-embedding:0.6b) — best-effort ==="
curl -sN http://ollama:11434/api/pull -d '{"name":"qwen3-embedding:0.6b"}' | while read -r line; do
status=$$(echo "$$line" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
[ -n "$$status" ] && echo " $$status"
done || echo " (pull failed — relying on the cached model)"
echo "=== Pulling LLM model (glm-5.3:cloud) — best-effort ==="
curl -sN http://ollama:11434/api/pull -d '{"name":"glm-5.3:cloud"}' | while read -r line; do
status=$$(echo "$$line" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
[ -n "$$status" ] && echo " $$status"
done || echo " (pull failed — relying on the cached model)"
echo "=== Verifying models are present (the real success gate) ==="
curl -sf http://ollama:11434/api/tags | grep -q "qwen3-embedding" || { echo "FATAL: qwen3-embedding missing and could not be pulled"; exit 1; }
curl -sf http://ollama:11434/api/tags | grep -q "glm-5.3" || { echo "FATAL: glm-5.3 missing and could not be pulled"; exit 1; }
echo "=== All models ready! ==="
# ==========================================================================
# SearXNG - Self-hosted metasearch for the web-research capability
# ==========================================================================
# The DEFAULT research provider (config default is searxng; the orchestrator
# env block points ROBOCO_RESEARCH_SEARXNG_BASE_URL at this container).
# Tavily/Brave/Exa remain opt-in via .env. Internal-only by design: no host port, never proxied by nginx:
# an exposed open metasearch instance is abuse bait. The JSON format and
# limiter-off live in deploy/searxng/settings.yml. The secret guards the
# instance's own cookies only; on an internal bridge with no auth surface
# the default is harmless, but override SEARXNG_SECRET in .env if the
# container is ever exposed deliberately.
# Resource note: ~150-300 MB RAM, one uwsgi worker, no GPU.
searxng:
image: searxng/searxng:latest
container_name: roboco-searxng
restart: unless-stopped
environment:
SEARXNG_SECRET: ${SEARXNG_SECRET:-roboco-internal-searxng}
SEARXNG_BASE_URL: http://roboco-searxng:8080/
volumes:
- ./deploy/searxng/settings.yml:/etc/searxng/settings.yml:ro
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 5s
retries: 5
start_period: 15s
# ==========================================================================
# Video Renderer - sidecar for the video-generation engine (renders
# motion/ compositions to MP4 with HyperFrames). Mirrors the ollama sidecar
# pattern: a plain HTTP service the orchestrator talks to, nothing more.
# Harmless when idle — it only renders when POSTed; the video_engine_*
# feature flags gate whether the orchestrator ever sends it work. No DB
# access, no git, no credentials — it only ever reads what it's POSTed.
# ==========================================================================
video-renderer:
build:
context: .
dockerfile: docker/video-renderer.Dockerfile
image: roboco-video-renderer
container_name: roboco-video-renderer
restart: unless-stopped
# Isolated sidecar network: headless Chrome here executes
# agent-authored composition HTML/JS. Only the orchestrator (also
# homed on `render`) can reach it; it can reach nothing else.
networks:
- render
# Chrome headless rendering can crash under Docker's default 64MB
# /dev/shm ("Chrome crashed"); give it real shared memory.
shm_size: "1gb"
mem_limit: "2g"
# Cap is env-overridable so a 1-CPU host (small cloud VM, Docker Desktop
# with a 1-CPU VM) still starts; Docker rejects a `cpus` value above the
# host's available count instead of clamping. Default 1 works everywhere;
# raise it on a multi-core box for faster renders.
cpus: "${ROBOCO_VIDEO_RENDERER_CPUS:-1}"
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3001/health').then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
# ==========================================================================
# Decisions sidecar (the built-in self-hosted tiers of the Decisions
# service) - CPU-only typed decisions over llama.cpp: a 27B Clef backend
# (sweet-spot default) and a 421M Laya backend (cheap fallback), each a
# llama-server speaking the System One protocol behind one proxy. Mirrors
# the ollama sidecar pattern: a plain HTTP service the orchestrator talks
# to, nothing more. Inert until ROBOCO_DECISIONS_ENABLED goes on; shipping
# it changes nothing by itself. No DB, no git, no credentials - it only
# ever reads what it's POSTed. NO `networks:` key: joins the implicit
# default network (roboco_default) next to ollama, NEVER roboco_data, and
# never exposed through nginx or on a host port.
# ==========================================================================
decisions:
build:
context: .
dockerfile: docker/decisions.Dockerfile
image: roboco-decisions
container_name: roboco-decisions
restart: unless-stopped
environment:
# Per-deploy RAM knobs. The 19GB Clef GGUF is the heavy tenant (mmap'd
# from the baked image, paged into RAM as the model touches it); the
# Laya backend is ~450MB. On a box that cannot hold Clef, set
# ROBOCO_SIDECAR_CLEF_ENABLED=false: the container serves the cheap
# Laya backend only and the orchestrator's tier ladder follows /health
# automatically (no config change above the sidecar). Both backends
# idle at ~zero CPU - llama.cpp is event-driven, inference only runs
# on a POST.
CLEF_ENABLED: ${ROBOCO_SIDECAR_CLEF_ENABLED:-true}
LAYA_ENABLED: ${ROBOCO_SIDECAR_LAYA_ENABLED:-true}
healthcheck:
test: ["CMD", "python", "-c", "import sys,urllib.request; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8100/health', timeout=3).status == 200 else 1)"]
interval: 15s
timeout: 5s
retries: 5
# Weights are baked into the image; the start period covers the 19GB
# Clef GGUF loading into RAM from disk.
start_period: 180s
# ==========================================================================
# Agent Base Image Builder (specialized images built on-demand by orchestrator)
# ==========================================================================
agent-base-image:
build:
context: .
dockerfile: docker/agent-base.Dockerfile
image: roboco-agent-base
container_name: roboco-agent-base-builder
entrypoint: ["/bin/sh", "-c", "echo 'Agent base image built successfully'"]
restart: "no"
# ==========================================================================
# Agent PM Image Builder (specialized image built on-demand by orchestrator)
# ==========================================================================
agent-pm-image:
build:
context: .
dockerfile: docker/agent-pm.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-pm
entrypoint: ["/bin/sh", "-c", "echo 'Agent PM image built'"]
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Backend Dev Image Builder (specialized image built on-demand by orchestrator)
# ==========================================================================
agent-dev-be-image:
build:
context: .
dockerfile: docker/agent-dev-be.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-dev-be
entrypoint: ["/bin/sh", "-c", "echo 'Agent Backend Dev image built'"]
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Frontend Dev Image Builder (specialized image built on-demand by orchestrator)
# ==========================================================================
agent-dev-fe-image:
build:
context: .
dockerfile: docker/agent-dev-fe.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-dev-fe
entrypoint: ["/bin/sh", "-c", "echo 'Agent Frontend Dev image built'"]
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Backend QA Image Builder (specialized image built on-demand by orchestrator)
# ==========================================================================
agent-qa-be-image:
build:
context: .
dockerfile: docker/agent-qa-be.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-qa-be
entrypoint: ["/bin/sh", "-c", 'echo "Agent Backend QA image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Frontend QA Image Builder (specialized image built on-demand by orchestrator)
# ==========================================================================
agent-qa-fe-image:
build:
context: .
dockerfile: docker/agent-qa-fe.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-qa-fe
entrypoint: ["/bin/sh", "-c", 'echo "Agent Frontend QA image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent UX/UI Dev Image Builder (specialized image built on-demand by orchestrator)
# ==========================================================================
agent-ux-image:
build:
context: .
dockerfile: docker/agent-ux.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-ux
entrypoint: ["/bin/sh", "-c", 'echo "Agent UX/UI image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent DevOps Image Builder (specialized image built on-demand by orchestrator)
# ==========================================================================
agent-devops-image:
build:
context: .
dockerfile: docker/agent-devops.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-devops
entrypoint: ["/bin/sh", "-c", 'echo "Agent DevOps image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Documenter Image Builder (specialized image built on-demand by orchestrator)
# ==========================================================================
agent-doc-image:
build:
context: .
dockerfile: docker/agent-doc.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-doc
entrypoint: ["/bin/sh", "-c", 'echo "Agent Documenter image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Intake (Prompter) Image Builder (persistent Agent-SDK driver the CEO
# chats with; not a one-shot `claude -p`)
# ==========================================================================
agent-prompter-image:
build:
context: .
dockerfile: docker/agent-prompter.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-prompter
entrypoint: ["/bin/sh", "-c", 'echo "Agent Intake image built"']
restart: "no"
depends_on:
- agent-base-image
agent-secretary-image:
build:
context: .
dockerfile: docker/agent-secretary.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-secretary
entrypoint: ["/bin/sh", "-c", 'echo "Agent Secretary image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent PR Reviewer Image Builder (read-only reviewer of inbound external PRs)
# ==========================================================================
agent-pr-reviewer-image:
build:
context: .
dockerfile: docker/agent-pr-reviewer.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-pr-reviewer
entrypoint: ["/bin/sh", "-c", 'echo "Agent PR Reviewer image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Grok Image Builder (xAI Grok Build via the official grok CLI)
# ==========================================================================
agent-grok-image:
build:
context: .
dockerfile: docker/agent-grok.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-grok
entrypoint: ["/bin/sh", "-c", 'echo "Agent Grok image built"']
restart: "no"
depends_on:
- agent-base-image
# Interactive Grok roles (intake/secretary) — panel-driven grok-CLI sessions;
# built FROM roboco-agent-grok, so they depend on the Grok runtime image.
agent-grok-prompter-image:
build:
context: .
dockerfile: docker/agent-grok-prompter.Dockerfile
additional_contexts:
roboco-agent-grok: "service:agent-grok-image"
image: roboco-agent-grok-prompter
entrypoint: ["/bin/sh", "-c", 'echo "Agent Grok Prompter image built"']
restart: "no"
depends_on:
- agent-grok-image
agent-grok-secretary-image:
build:
context: .
dockerfile: docker/agent-grok-secretary.Dockerfile
additional_contexts:
roboco-agent-grok: "service:agent-grok-image"
image: roboco-agent-grok-secretary
entrypoint: ["/bin/sh", "-c", 'echo "Agent Grok Secretary image built"']
restart: "no"
depends_on:
- agent-grok-image
# ==========================================================================
# Agent Codex Image Builder (OpenAI via the official codex CLI). One-shot
# delivery roles only in V1 — no interactive prompter/secretary variant.
# ==========================================================================
agent-codex-image:
build:
context: .
dockerfile: docker/agent-codex.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-codex
entrypoint: ["/bin/sh", "-c", 'echo "Agent Codex image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Gemini Image Builder (Google Gemini via the official gemini CLI).
# One-shot delivery roles only (V1) — no interactive prompter/secretary
# variant exists for Gemini yet, contrast the Grok images above.
# ==========================================================================
agent-gemini-image:
build:
context: .
dockerfile: docker/agent-gemini.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-gemini
entrypoint: ["/bin/sh", "-c", 'echo "Agent Gemini image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Kimi Image Builder (Moonshot AI Kimi K3 via the official kimi CLI).
# One-shot delivery roles only (V1) — no interactive prompter/secretary
# variant exists for Kimi yet, contrast the Grok images above.
# ==========================================================================
agent-kimi-image:
build:
context: .
dockerfile: docker/agent-kimi.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-kimi
entrypoint: ["/bin/sh", "-c", 'echo "Agent Kimi image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent OpenRouter Image Builder (aggregated catalog via the official
# opencode CLI). One-shot delivery roles only (V1) — no interactive
# prompter/secretary variant exists for OpenRouter, contrast the Grok
# images above.
# ==========================================================================
agent-openrouter-image:
build:
context: .
dockerfile: docker/agent-openrouter.Dockerfile
additional_contexts:
roboco-agent-base: "service:agent-base-image"
image: roboco-agent-openrouter
entrypoint: ["/bin/sh", "-c", 'echo "Agent OpenRouter image built"']
restart: "no"
depends_on:
- agent-base-image
# ==========================================================================
# Agent Nebius Image Builder (Token Factory via the opencode CLI).
# Builds the image a Nebius-routed spawn runs; routing to Nebius stays
# opt-in (key + Nebius mode or pins). Without it no compose service
# references roboco-agent-nebius, so deploy-nas.sh never builds it and
# the -live pass defers on its base.
# ==========================================================================
agent-nebius-image:
build:
context: .
dockerfile: docker/agent-nebius.Dockerfile
additional_contexts: