@orchestraworks/cli 0.5.1 → 0.5.3

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 (112) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/api/compute-provider-check.d.ts +29 -0
  3. package/dist/cli/api/compute-provider-check.js +207 -0
  4. package/dist/cli/api/tail.d.ts +66 -0
  5. package/dist/cli/api/tail.js +564 -0
  6. package/dist/cli/main.d.ts +5 -1
  7. package/dist/cli/main.js +22 -0
  8. package/dist/constructs/agent.d.ts +10 -0
  9. package/dist/constructs/agent.js +39 -4
  10. package/dist/constructs/environment.d.ts +29 -1
  11. package/dist/constructs/environment.js +28 -1
  12. package/dist/constructs/skill.d.ts +6 -1
  13. package/dist/constructs/skill.js +21 -2
  14. package/dist/engine/reconcile.js +15 -1
  15. package/dist/generated/cli-commands.json +876 -1
  16. package/dist/generated/managed-skills.d.ts +2 -0
  17. package/dist/generated/managed-skills.js +13 -0
  18. package/dist/index.d.ts +1 -1
  19. package/docs/REFERENCE.md +21 -1
  20. package/node_modules/@orchestraworks/client/dist/esm/models/ApiKey.d.ts +6 -0
  21. package/node_modules/@orchestraworks/client/dist/esm/models/ApiKey.js +2 -0
  22. package/node_modules/@orchestraworks/client/dist/esm/models/ComputeProvider.d.ts +3 -3
  23. package/node_modules/@orchestraworks/client/dist/esm/models/CreateApiKeyRequest.d.ts +7 -1
  24. package/node_modules/@orchestraworks/client/dist/esm/models/CreateApiKeyRequest.js +2 -0
  25. package/node_modules/@orchestraworks/client/dist/esm/models/CreateEnvironmentRequest.d.ts +13 -0
  26. package/node_modules/@orchestraworks/client/dist/esm/models/CreateEnvironmentRequest.js +5 -0
  27. package/node_modules/@orchestraworks/client/dist/esm/models/Environment.d.ts +13 -0
  28. package/node_modules/@orchestraworks/client/dist/esm/models/Environment.js +5 -0
  29. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentHook.d.ts +38 -0
  30. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentHook.js +45 -0
  31. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentHooks.d.ts +39 -0
  32. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentHooks.js +44 -0
  33. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentHooksRequest.d.ts +39 -0
  34. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentHooksRequest.js +44 -0
  35. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentProvisionSpec.d.ts +2 -1
  36. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentProvisionSpecRequest.d.ts +2 -1
  37. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentSnapshot.d.ts +19 -0
  38. package/node_modules/@orchestraworks/client/dist/esm/models/EnvironmentSnapshot.js +7 -0
  39. package/node_modules/@orchestraworks/client/dist/esm/models/Machine.d.ts +7 -1
  40. package/node_modules/@orchestraworks/client/dist/esm/models/Machine.js +2 -0
  41. package/node_modules/@orchestraworks/client/dist/esm/models/UpdateEnvironmentRequest.d.ts +13 -0
  42. package/node_modules/@orchestraworks/client/dist/esm/models/UpdateEnvironmentRequest.js +5 -0
  43. package/node_modules/@orchestraworks/client/dist/esm/models/index.d.ts +3 -0
  44. package/node_modules/@orchestraworks/client/dist/esm/models/index.js +3 -0
  45. package/node_modules/@orchestraworks/client/dist/models/ApiKey.d.ts +6 -0
  46. package/node_modules/@orchestraworks/client/dist/models/ApiKey.js +2 -0
  47. package/node_modules/@orchestraworks/client/dist/models/ComputeProvider.d.ts +3 -3
  48. package/node_modules/@orchestraworks/client/dist/models/CreateApiKeyRequest.d.ts +7 -1
  49. package/node_modules/@orchestraworks/client/dist/models/CreateApiKeyRequest.js +2 -0
  50. package/node_modules/@orchestraworks/client/dist/models/CreateEnvironmentRequest.d.ts +13 -0
  51. package/node_modules/@orchestraworks/client/dist/models/CreateEnvironmentRequest.js +5 -0
  52. package/node_modules/@orchestraworks/client/dist/models/Environment.d.ts +13 -0
  53. package/node_modules/@orchestraworks/client/dist/models/Environment.js +5 -0
  54. package/node_modules/@orchestraworks/client/dist/models/EnvironmentHook.d.ts +38 -0
  55. package/node_modules/@orchestraworks/client/dist/models/EnvironmentHook.js +52 -0
  56. package/node_modules/@orchestraworks/client/dist/models/EnvironmentHooks.d.ts +39 -0
  57. package/node_modules/@orchestraworks/client/dist/models/EnvironmentHooks.js +51 -0
  58. package/node_modules/@orchestraworks/client/dist/models/EnvironmentHooksRequest.d.ts +39 -0
  59. package/node_modules/@orchestraworks/client/dist/models/EnvironmentHooksRequest.js +51 -0
  60. package/node_modules/@orchestraworks/client/dist/models/EnvironmentProvisionSpec.d.ts +2 -1
  61. package/node_modules/@orchestraworks/client/dist/models/EnvironmentProvisionSpecRequest.d.ts +2 -1
  62. package/node_modules/@orchestraworks/client/dist/models/EnvironmentSnapshot.d.ts +19 -0
  63. package/node_modules/@orchestraworks/client/dist/models/EnvironmentSnapshot.js +7 -0
  64. package/node_modules/@orchestraworks/client/dist/models/Machine.d.ts +7 -1
  65. package/node_modules/@orchestraworks/client/dist/models/Machine.js +2 -0
  66. package/node_modules/@orchestraworks/client/dist/models/UpdateEnvironmentRequest.d.ts +13 -0
  67. package/node_modules/@orchestraworks/client/dist/models/UpdateEnvironmentRequest.js +5 -0
  68. package/node_modules/@orchestraworks/client/dist/models/index.d.ts +3 -0
  69. package/node_modules/@orchestraworks/client/dist/models/index.js +3 -0
  70. package/node_modules/@orchestraworks/client/package.json +1 -1
  71. package/package.json +4 -4
  72. package/skills/orchestra/SKILL.md +167 -42
  73. package/skills/orchestra-fdk/SKILL.md +112 -545
  74. package/skills/orchestra-fdk/references/authoring.md +130 -3
  75. package/skills/orchestra-fdk/references/environment.md +352 -43
  76. package/skills/orchestra-fdk/references/integrations/analytics.md +18 -0
  77. package/skills/orchestra-fdk/references/integrations/ci-cd.md +20 -0
  78. package/skills/orchestra-fdk/references/integrations/cloud.md +19 -0
  79. package/skills/orchestra-fdk/references/integrations/code-repo.md +76 -0
  80. package/skills/orchestra-fdk/references/integrations/databases.md +22 -0
  81. package/skills/orchestra-fdk/references/integrations/dependencies.md +29 -0
  82. package/skills/orchestra-fdk/references/integrations/design.md +18 -0
  83. package/skills/orchestra-fdk/references/integrations/feature-flags.md +19 -0
  84. package/skills/orchestra-fdk/references/integrations/incident.md +29 -0
  85. package/skills/orchestra-fdk/references/integrations/issue-tracker.md +65 -0
  86. package/skills/orchestra-fdk/references/integrations/knowledge-base.md +43 -0
  87. package/skills/orchestra-fdk/references/integrations/messaging.md +38 -0
  88. package/skills/orchestra-fdk/references/integrations/observability.md +36 -0
  89. package/skills/orchestra-fdk/references/integrations/secrets-managers.md +25 -0
  90. package/skills/orchestra-fdk/references/integrations/support-desk.md +31 -0
  91. package/skills/orchestra-fdk/references/phases/0-start.md +62 -0
  92. package/skills/orchestra-fdk/references/phases/1-discover.md +111 -0
  93. package/skills/orchestra-fdk/references/phases/2-plan.md +157 -0
  94. package/skills/orchestra-fdk/references/phases/3-connect.md +99 -0
  95. package/skills/orchestra-fdk/references/phases/4-build.md +159 -0
  96. package/skills/orchestra-fdk/references/phases/5-prove.md +107 -0
  97. package/skills/orchestra-fdk/references/phases/6-handover.md +42 -0
  98. package/skills/orchestra-fdk/references/troubleshooting.md +128 -0
  99. package/templates/bug-fix/agents/bugfix.md +1 -1
  100. package/templates/bug-fix/main.ts +1 -1
  101. package/templates/engineering-team/README.md +45 -181
  102. package/templates/engineering-team/agents/engineer-agent.md +10 -7
  103. package/templates/engineering-team/agents/manager-agent.md +23 -120
  104. package/templates/engineering-team/inputs.json +1 -3
  105. package/templates/engineering-team/lib/environment.ts +10 -90
  106. package/templates/engineering-team/lib/inputs.ts +5 -97
  107. package/templates/engineering-team/lib/setup.sh +5 -5
  108. package/templates/engineering-team/main.ts +22 -69
  109. package/templates/todo-factory/agents/bugfix.md +1 -1
  110. package/templates/todo-factory/main.ts +1 -1
  111. package/templates/engineering-team/agents/engineer-bare.md +0 -22
  112. package/templates/engineering-team/onboarding/create-score-metrics.sh +0 -33
@@ -103,6 +103,21 @@ entrypoint decides what answers. Omit `harness`, or misspell it, and synth fails
103
103
  a message naming the five; there is no default, because guessing which one an author meant would pick a
104
104
  different program than the one they wanted.
105
105
 
106
+ ### Every agent's instructions start with an Attribution section
107
+
108
+ The starter's agents start their instructions with it. When you write an agent of your own, start its
109
+ instructions with the same section, with the agent's display name and slug filled in:
110
+
111
+ ```
112
+ ## Attribution
113
+ You are <agent display name>. Mark the work you produce as yours:
114
+ - every commit message and pull request description ends with
115
+ Co-Authored-By: <agent display name> <<agent-slug>@agents.orchestraworks.ai>
116
+ - every comment and tracker edit ends with one line: — <agent display name>, Orchestra
117
+ ```
118
+
119
+ A person who reads a commit, a pull request, a comment or a tracker edit then sees which agent made it.
120
+
106
121
  ### Platform skills are attached by id
107
122
 
108
123
  An agent uses a platform skill only when its `skills` list names it: `Skill.fromId(factory, "skill_<key>")`
@@ -516,7 +531,7 @@ const claudeSignIn = new Secret(factory, "claude-sign-in", {
516
531
  });
517
532
 
518
533
  // …in the Environment's props:
519
- env: { CLAUDE_CODE_OAUTH_TOKEN: "placeholder", ANTHROPIC_API_KEY: "" },
534
+ env: { CLAUDE_CODE_OAUTH_TOKEN: "software-factory-egress-placeholder", ANTHROPIC_API_KEY: "" },
520
535
  egress: EgressPolicy.allowAll()
521
536
  .allow("claude-sign-in", { hosts: ["api.anthropic.com"], priority: 300, credential: claudeSignIn }),
522
537
  ```
@@ -604,6 +619,106 @@ which the harness would otherwise send instead.
604
619
  (the recipe supplies the headers), or use `bearer(header, secret)` / `basic(...)` / `header(...)`
605
620
  for one-header bindings, or `inject(secret, recipe)` for a binding-owned recipe.
606
621
 
622
+ ## How the agents reach a model
623
+
624
+ Ask the person which of three ways they want, and walk them through its steps once.
625
+
626
+ **1. Subscription: their own Claude or Codex sign-in.** The person makes the sign-in in a real terminal
627
+ on their own computer; run from your shell, its browser step hangs. Never read or copy a value yourself.
628
+
629
+ - **Claude: a long-lived token in a Secret, the recommended way.** `claude setup-token` prints an
630
+ `sk-ant-oat…` token made for servers. It lasts about a year, and nothing on a machine renews or revokes
631
+ it, so it needs no shared `/home`. The person copies it and saves it to a file only they can read,
632
+ without pasting it into a chat: `umask 077 && pbpaste | tr -d '\n' > <file> && pbcopy </dev/null`.
633
+ At apply, the shell exports it from that file into the variable the Secret reads. Declare the Claude
634
+ sign-in recipe (§ Secrets are recipes, never values): a `Secret`, an egress rule that allows
635
+ `api.anthropic.com` and sets `Authorization: Bearer ${secret}` with `x-api-key` removed, and, in the
636
+ environment's `env`, `CLAUDE_CODE_OAUTH_TOKEN: "software-factory-egress-placeholder"` and an empty
637
+ `ANTHROPIC_API_KEY`. Turn `enableModelGateway` off for the agent. The machine holds only the
638
+ placeholder; the edge adds the token. Copying the renewing credentials file instead is the fallback,
639
+ and follows the ChatGPT sign-in steps below.
640
+ - **Codex: a personal access token, where the workspace allows one.** A ChatGPT Business or Enterprise
641
+ member creates it at chatgpt.com/admin/access-tokens, once the workspace owner allows access tokens,
642
+ and chooses how long it lasts, such as 7 to 90 days. Codex never renews it, so it needs no shared
643
+ `/home`; the person supplies a new one before it expires. It begins `at-` and goes in
644
+ `ORCHESTRA_WORKER_CODEX_PERSONAL_ACCESS_TOKEN`, which outranks a ChatGPT sign-in or an API key beside
645
+ it. On their own computer, `codex login --with-access-token` reads the same token from standard input.
646
+ - **Codex otherwise: a separate ChatGPT sign-in on a shared `/home`.** The other long-lived choice is an
647
+ OpenAI API key, billed by use rather than by the subscription (option 3).
648
+ 1. **Make a separate sign-in, never a copy of the local one:** `mkdir -p <dir> && CODEX_HOME=<dir>
649
+ codex login`, with `--device-auth` only on a machine with no browser. Its tokens in
650
+ `<dir>/auth.json` go in the `ORCHESTRA_WORKER_CODEX_` variables below, or a setup step writes the
651
+ file.
652
+ 2. **Put it on a shared `/home`.** Give the environment a durable `/home` volume, shared by every
653
+ machine that uses the sign-in, so one file renews for all of them. Separate copies fight: each
654
+ renewal replaces the refresh token, so the first copy to renew signs out the others. Two machines
655
+ that renew in the same second can still sign each other out; then make a new separate sign-in.
656
+ Remove the variables after the first machine starts, or each new machine writes the old token back.
657
+ 3. **Never sign in again in the default folder after the copy.** A new `codex login` in `~/.codex`
658
+ revokes the cloud copy, and every machine then answers "refresh token was revoked". If it
659
+ happened, make a new separate sign-in.
660
+ - **Keep a Codex sign-in off an agent that uses the model gateway.** With a ChatGPT sign-in or a personal
661
+ access token, Codex compresses its requests, and the gateway refuses them with `model_not_found`, a
662
+ known issue. Give such an agent the subscription directly, with `enableModelGateway` off, or keep the
663
+ homes apart.
664
+
665
+ **2. Model gateway: Orchestra provides the key.** One endpoint reaches OpenAI, Anthropic and open-source
666
+ models, on the organization's credits. The person can also bring their own provider key to the gateway:
667
+ `orchestra fdk init --model-key anthropic` (or `openai`) declares a `ModelProviderKey`, which the gateway uses for
668
+ that provider's calls. The gateway also routes models: `orchestra model-configs` sets which providers serve a
669
+ model and in what order, so a call falls back to the next one when a provider is down, and
670
+ `orchestra model-aliases` gives a model a name of the organization's own, such as `default`, that one change
671
+ points elsewhere.
672
+
673
+ **3. Own API key via the vault.** Their Anthropic or OpenAI API key is a platform `Secret`, added at the
674
+ network edge on calls straight to the provider, with no gateway in between, and nothing to sync, because
675
+ an API key does not renew. Declare it as the Claude sign-in recipe (§ Secrets are recipes) does, with the provider's own
676
+ header, and leave `enableModelGateway` off in the agent's model settings:
677
+
678
+ ```ts
679
+ const anthropicKey = new Secret(factory, "anthropic-api-key", {
680
+ value: SecretValue.fromEnv("ORCHESTRA_FDK_ANTHROPIC_API_KEY"),
681
+ injection: { headers: [{ name: "x-api-key", value: "${secret}" }] },
682
+ });
683
+ // …in the Environment's props:
684
+ egress: EgressPolicy.allowAll()
685
+ .allow("anthropic", { hosts: ["api.anthropic.com"], priority: 300, credential: anthropicKey }),
686
+ ```
687
+
688
+ For OpenAI, inject `authorization: Bearer ${secret}` on `api.openai.com`. Recommend this over a
689
+ subscription when the person has an API key: there is no file to copy and no shared `/home`.
690
+
691
+ ### The subscription variables
692
+
693
+ The worker writes each value into the file the harness reads, once per machine start, and hands none of
694
+ these variables to the harness. pi can also run on the provider you name.
695
+
696
+ | Variable | claude | codex | pi |
697
+ |---|---|---|---|
698
+ | `ORCHESTRA_WORKER_CLAUDE_OAUTH_TOKEN`: a `claude setup-token` token as a worker setting, in plain text; the Claude sign-in recipe's Secret is the recommended way | yes | — | yes |
699
+ | `ORCHESTRA_WORKER_CLAUDE_ACCESS_TOKEN` and `ORCHESTRA_WORKER_CLAUDE_REFRESH_TOKEN`, plus `ORCHESTRA_WORKER_CLAUDE_EXPIRES_AT` in milliseconds if you have it: a renewing Claude sign-in | yes | — | yes |
700
+ | `ORCHESTRA_WORKER_OPENAI_API_KEY` | — | yes | yes |
701
+ | `ORCHESTRA_WORKER_CODEX_PERSONAL_ACCESS_TOKEN`: a Codex personal access token, which outranks the other Codex rows | — | yes | — |
702
+ | `ORCHESTRA_WORKER_CODEX_ID_TOKEN`, `ORCHESTRA_WORKER_CODEX_ACCESS_TOKEN`, `ORCHESTRA_WORKER_CODEX_REFRESH_TOKEN` and `ORCHESTRA_WORKER_CODEX_ACCOUNT_ID` (`tokens.account_id`, which Codex needs to renew): a ChatGPT sign-in | — | yes | — |
703
+ | `ORCHESTRA_WORKER_PI_PROVIDER`: pi's `defaultProvider`, such as `anthropic` or `openai` | — | — | yes |
704
+
705
+ You can also bake the harness's own sign-in or settings file into the image with the setup script. With
706
+ no variable set, the worker leaves a value already in that file alone. For pi, your own
707
+ `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_OAUTH_TOKEN`, `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` in `env` still works: the worker then leaves that provider's
708
+ placeholder out of pi's sign-in file, which pi would otherwise read first, and holds back the image's
709
+ `ANTHROPIC_OAUTH_TOKEN` placeholder beside your own `ANTHROPIC_API_KEY`.
710
+
711
+ - **The value is plain text.** `env` is stored unencrypted, and an environment read returns it. Anyone
712
+ who can read the environment can read its setup script.
713
+ - **Allow the sign-in hosts with no credential bound**: `platform.claude.com` for a copied Claude
714
+ credentials file, `api.openai.com` for an OpenAI key, `auth.openai.com` and `chatgpt.com` for ChatGPT. A
715
+ rule that binds a credential on one of these hosts replaces your sign-in on every call.
716
+ - **Name pi's provider when the model is not a Claude one.** pi runs the agent's model under its
717
+ `defaultProvider`, which `ORCHESTRA_WORKER_PI_PROVIDER` sets; with no variable, pi keeps the image's
718
+ `anthropic` or the provider you configured. The platform never picks it from the model.
719
+ - **On a Mac outpost, Claude Code reads the login Keychain first.** Sign out of Claude Code there, or
720
+ use `ORCHESTRA_WORKER_CLAUDE_OAUTH_TOKEN`, which Claude Code reads before the Keychain.
721
+
607
722
  ## Egress: apply the bundles, hand-write the rest
608
723
 
609
724
  An environment's `egress` is a policy you build up from one of **two starting points**:
@@ -977,9 +1092,9 @@ cookie your app sets must carry `SameSite=None; Secure; Partitioned`, or the bro
977
1092
  frame and your app's own sign-in or session state is lost on each load. The platform's own access
978
1093
  cookie is already set this way, and your app never sees it.
979
1094
 
980
- ### Typed flows: `user-grants grant` and `sessions wait`
1095
+ ### Typed flows: `user-grants grant`, `sessions wait` and `sessions tail`
981
1096
 
982
- Two commands stay hand-shaped, because the orchestration is the value a bare generated call would
1097
+ Three commands stay hand-shaped, because the orchestration is the value a bare generated call would
983
1098
  drop. Both are platform commands — they read and write the live factory, not your code.
984
1099
 
985
1100
  ```bash
@@ -1004,6 +1119,18 @@ taking no more messages), which settles too. `--interval` and `--timeout` are yo
1004
1119
  any settlement (the printed state
1005
1120
  carries the outcome) and non-zero only on a timeout or a failed call.
1006
1121
 
1122
+ ```bash
1123
+ orchestra sessions tail session_01abc --until-settled
1124
+ ```
1125
+
1126
+ Prints each item of the session as it is saved: a reply line by line as it grows, a tool call as one
1127
+ line, printed again when its status changes. With `--output json` it prints each item once, as one
1128
+ JSON line, when the item can no longer change. It starts with the items that
1129
+ already exist; `--after <item id>` starts after that item instead. While a turn runs, each read waits
1130
+ on the platform for the next saved output, so the command does not poll on a timer. `--until-settled`
1131
+ exits once the turn has ended and prints the session's status, with exit 0 whatever the status, as
1132
+ `sessions wait` does. Without it, the command follows the session until Ctrl-C.
1133
+
1007
1134
  ### The backup: `orchestra api`
1008
1135
 
1009
1136
  `orchestra api GET "/sessions?limit=5"` makes a raw authenticated call — exactly `gh api`'s role — through
@@ -1,7 +1,7 @@
1
1
  # Port a codebase's setup to Orchestra machines
2
2
 
3
- Step 8 of the orchestra-fdk skill, "Set up the machines", sends you here, and so does its step 3 for a new
4
- environment in an existing factory. When you finish, go back to the step that sent you.
3
+ Phase 4 of the orchestra-fdk skill, "Build", sends you here, for a new factory and for a new environment in
4
+ an existing factory. When you finish section 6, go back to phase 4 for the apply.
5
5
 
6
6
  An agent can fix, test or show a codebase only if its machine can install and run it. You find out how
7
7
  this team sets up their code, prove it on this computer first, write it into the factory, and apply it.
@@ -319,7 +319,7 @@ Orchestra Secret, and our network edge adds it to each model call, so no machine
319
319
 
320
320
  - For the gateway, `apply` creates the gateway key and its secret. The starter already declares them.
321
321
  - For a subscription token or a provider key, declare a Secret and a rule that adds it on the provider's
322
- host, as step 6 of the orchestra-fdk skill says (orchestra skill, sections 6 and 7).
322
+ host, as phase 2 of the orchestra-fdk skill, "The model route", says (orchestra skill, sections 6 and 7).
323
323
 
324
324
  Ask the developer to revoke the `local-dev` gateway key, at **Settings › Gateway keys**, when local work is
325
325
  done.
@@ -332,6 +332,116 @@ fails and why. Report which tests ran and which could not. Then run `orchestra l
332
332
  Ask the developer to confirm the path you recommend, with your question tool. The mode you proved points
333
333
  to the path, as the table in section 2 says: a container machine to A, the host outpost to B.
334
334
 
335
+ ### The machine's runtime, by operating system
336
+
337
+ An environment's `platform` is `{ os, arch, runtime }`, set when the environment is created. `runtime` never
338
+ changes. On a provisioned environment it is `microvm` (the default) or `vm`. An enrolled environment is
339
+ `host`: the team's own machine, as it is, with no network rules of ours.
340
+
341
+ The setup script runs once, as root or as the machine's service account, on each new machine, before the
342
+ agent arrives and before any code is cloned. It is at most 64 KiB. It has 10 minutes. The limit is fixed for
343
+ now, and an environment cannot raise it. A worker that starts again on the same
344
+ machine reuses the result it kept, and does not run the script again.
345
+
346
+ #### Linux
347
+
348
+ Most code: web, backend, mobile build tools, anything that runs in Docker.
349
+
350
+ | Runtime and where it runs | Base image: what's in it | Sizes | Disks and shared volumes | Setup script | Home and continuity | Requirements and limits |
351
+ |---|---|---|---|---|---|---|
352
+ | `microvm`: a Kata micro VM on Kubernetes, amd64. `vm`: an EC2 Ubuntu VM, amd64 or arm64 | `microvm`: Debian with Node 24, Java 21 and 25, Python 3, a C and C++ build toolchain, git, the GitHub CLI `gh`, and Docker with Compose and Buildx (not started until the setup script starts it). `vm`: Ubuntu 24.04 with Node 24, git, and rootless Docker with Buildx, which runs from boot as the agent's user. The VM has no Java and no Compose plugin: install the ones the code needs in the setup script. Both carry the worker and the `claude`, `codex`, `pi` and `script` harnesses | `microvm`: 0.5, 1, 2, 4, 8, 16 or 32 CPU. `vm`: 2, 4 or 8 CPU. 2 GB of memory per CPU | `vm`: a 40 GiB root disk. Up to four extra disks (`disks`), such as `/var/lib/docker` and `/work`. Shared volumes (`volumeMounts`) on EFS, which every machine of an environment can mount together | Bash, run with `bash -e`. One exit code per step | A volume at `/home` keeps the agent's home across machines. Without it, nothing in `/home` outlives the machine | No Windows or macOS programs. A kernel of its own, an Android device or nested virtualization needs `vm` |
353
+
354
+ #### Windows
355
+
356
+ Code that builds or runs only on Windows: .NET Framework, WinForms, WPF, IIS, Windows-only tools.
357
+
358
+ | Runtime and where it runs | Base image: what's in it | Sizes | Disks and shared volumes | Setup script | Home and continuity | Requirements and limits |
359
+ |---|---|---|---|---|---|---|
360
+ | `vm`: an EC2 Windows Server VM, amd64. Set `platform: { os: "windows", arch: "amd64", runtime: "vm" }` | Our image only: Windows Server 2025 Core, Node 24, Docker for Windows containers with the Containers feature, the worker, the `orchestra` command, and the `claude`, `codex`, `pi` and `script` harnesses. No git: install it in the setup script. A hosted Windows machine cannot start from another image | 4 or 8 CPU, 2 GB of memory per CPU | The image's 100 GiB root disk. No extra disks: remove `disks`. Shared volumes: not stated | PowerShell 5.1, run as the machine's service account. One exit code per step. Template: "A Windows setup script" below | The machine is parked when idle and keeps its disk, so the home (`C:\sf\home`) and `C:\work` stay. The setup script runs once per machine | Docker runs Windows containers only, for administrators only. A machine takes up to 15 minutes to start the first time |
361
+
362
+ **Windows in three stages.** Guide a Windows team through these stages, in order. Write the stage you plan
363
+ into `orchestra-factory-notes.md`.
364
+
365
+ 1. **Start on hosted Windows, with our image.** This proves the factory works, with nothing to run in the
366
+ team's own cloud. The setup script installs what fits in its 10 minutes: Git, and the small tools. A
367
+ larger toolchain, such as Visual Studio Build Tools or an old SDK, goes in the agent's first turn. The
368
+ machine is parked when idle and keeps its disk, so the next turn finds the toolchain installed. The agent
369
+ runs the install in the turn and waits for it: a park stops the VM and kills an install still running.
370
+ The machine is deleted after `ttl`, 2 hours by default, and a new one starts without the toolchain.
371
+ 2. **Then move to the team's own Windows image, through a compute provider in their cloud.** The benefit:
372
+ each machine starts with the whole toolchain installed, with no setup limit, and the team's licenses,
373
+ such as Visual Studio's, stay in their own image. The Launcher, the program the team runs in their
374
+ cloud, starts Windows VMs from the image its environment names. The Launcher runs on Azure (the
375
+ orchestra skill, section 18). The team bakes the image on Windows Server 2025 and adds their own
376
+ toolchain: git, MSBuild and .NET are theirs to install. Windows 11 is not supported, and a plain
377
+ captured Windows VM does not register. The image must contain these three parts:
378
+ - The worker's release package, for example at `C:\ProgramData\sfdk\worker\package`.
379
+ - The worker's Windows service, `SfdkWorker`. It runs `worker.exe run --root C:\ProgramData\sfdk\worker
380
+ --service` as LocalSystem. It is demand-start (`Manual`), not automatic. On a failure it restarts
381
+ three times, 2 seconds apart, and a clean stop with a non-zero exit code counts as a failure.
382
+ - The startup task `orchestra-azure-userdata`. It runs `azure-userdata.ps1` as SYSTEM at each boot
383
+ (`AtStartup`). The script writes the files that the VM's startup data carries, then starts
384
+ `SfdkWorker`.
385
+
386
+ Our image builds the same parts: `install-vm-runtime.ps1` and the image template
387
+ `infra/packer/azure-windows-runtime.pkr.hcl`. Under a Launcher, the image needs no WinDivert, no
388
+ redirect firewall rule, no Docker, no Node and no preinstalled harness: a harness installs itself on
389
+ its first turn, in about 60 seconds. Credentials are the team's to set on the VM (part C, "Tokens on a
390
+ compute provider's machines"). Our own image is in a public Azure gallery:
391
+ `/CommunityGalleries/orchestraworks-881a796f-0a84-4be1-af80-0a38564a406a/Images/runtime-win/Versions/0.0.136`
392
+ (Gen2). An environment that names no image boots its `Versions/latest`. Not released yet: a one-command
393
+ installer for a team's own image, the Launcher on AWS, and a team's own image on hosted Windows.
394
+ 3. **Then keep machines ready, for speed.** `standbyCapacity` on the agent (`Agent` in the kit) keeps
395
+ `count` machines started, set up and with the agent installed, on a schedule or always. A new
396
+ conversation then starts at once, with no wait for a Windows first boot. The platform refuses it only
397
+ for an enrolled environment or a `custom` harness, so it applies to hosted Windows and to a compute
398
+ provider. Proven on a compute provider's Windows VM: a new conversation got a ready machine, and its
399
+ turn started in 3.5 seconds. A standby minute is billed: the plan page shows the price.
400
+ `npx orchestra agents get <agent>` shows `standbyStatus`: how many machines are ready now, and why
401
+ fewer when that is so.
402
+
403
+ #### macOS
404
+
405
+ Code that needs Xcode, the iOS Simulator or another Apple tool.
406
+
407
+ Not released yet: Orchestra launches no Mac. A Mac works only as the team's own enrolled machine
408
+ (`host`), with the worker installed on it, no network rules of ours and no keys added at the edge. Agree it
409
+ with the Orchestra team first (the orchestra skill, section 18).
410
+
411
+ #### A custom image
412
+
413
+ A team's own image, when the stock image plus a setup script is too slow or cannot hold the toolchain.
414
+
415
+ - Build it `FROM` our runtime image, so it keeps the worker and the harnesses. The public name is
416
+ `public.ecr.aws/orchestraworks/runtime` (until AWS approves that alias: `public.ecr.aws/z8d3m6s5/runtime`).
417
+ Pin it by digest; the Orchestra team gives the current one.
418
+ - Push it to the team's own registry. `Image.fromBuild` builds and pushes it at apply. The environment names
419
+ a pull key Secret in `imagePullCredential` (`provisionSpec.imagePullSecretId` on the platform), made with
420
+ `SecretValue.registryCredential(<user>, <password>)` or `SecretValue.ecrCredential(<key id>, <secret>)`.
421
+ The platform reads it at each machine create, and never gives it to the agent. The bug-fix starter's
422
+ `main.ts` shows the code.
423
+ - Not released yet: a registry hosted by Orchestra.
424
+ - A custom image applies to `microvm`. An EC2 VM starts from our machine image, not from a container image.
425
+
426
+ #### On this computer
427
+
428
+ `orchestra local` runs Linux containers only, on Docker Desktop, OrbStack or Docker Engine. On an
429
+ Apple-silicon Mac it runs an `amd64` environment natively as `arm64` when the image carries that pair, and
430
+ never by emulation. Docker set to Windows containers is refused.
431
+
432
+ A Windows agent runs locally only as an enrolled machine: `orchestra outpost up --agent <id>` on a Windows
433
+ PC, against the local environment (section 4, "Host outpost"). Use it to work out the build, run and test
434
+ commands before the cloud.
435
+
436
+ Locally the network edge does nothing: no egress rule is enforced, no key is added, and no certificate is
437
+ re-signed. The cloud proves those.
438
+
439
+ #### Which to pick
440
+
441
+ Pick `microvm` when the code runs on Linux, with or without Docker. This is most code.
442
+ Pick `vm` when the code needs Windows, an Android device or a kernel of its own.
443
+ Pick `host` when the code must run on the team's own machine, such as a Mac.
444
+
335
445
  ### A. Containers on Linux
336
446
 
337
447
  Choose A when the code and its services run on Linux, in Docker, Docker Compose, or a setup script. Most
@@ -349,7 +459,7 @@ agent can run the team's own `docker compose up` unchanged.
349
459
  machine's processes, the agent included, read every value.
350
460
  3. Put each dev server's port in `PORTS`, with a short name. The agent can then show the app at a
351
461
  *machine endpoint*, a web address that only signed-in members of the organization can open.
352
- 4. Write the app's own settings files (`.env.local`) into the Set up item's instructions, because the code
462
+ 4. Write the app's own settings files (`.env.local`) into the Set up item's description, because the code
353
463
  is not on the machine when the setup script runs. Put only local or test values there. A real secret
354
464
  becomes a Secret plus a rule that adds it.
355
465
  5. Choose the size by what CI uses for the same work. The template's sizes are fixed per agent: lg is
@@ -358,6 +468,28 @@ agent can run the team's own `docker compose up` unchanged.
358
468
  the inner Docker's images across machine starts (next part).
359
469
  7. Not released yet: `workingDirectory`, the folder where every conversation starts. Today each agent clones
360
470
  or uses its code under `/work` in its own first turn, as the starter's agents do.
471
+ 8. To keep the agent's home, such as a harness sign-in, across machines, mount one volume at `/home` on
472
+ every environment that shares it. Make the volume once with `npx orchestra volumes create`, then add
473
+ `volumeMounts: [{ volume: Volume.fromSlug(factory, "<slug>"), mountPath: "/home" }]`. Without it,
474
+ nothing in `/home` outlives the machine.
475
+
476
+ #### Network
477
+
478
+ Egress starts open, and stays open for the first run: `EgressPolicy.allowAll()`. Any host is reachable, and
479
+ only the hosts that need a key, such as the model gateway, have rules that add one. Never close it while the
480
+ factory is being set up.
481
+
482
+ Tightening is a later, optional step, after the factory works and the developer asks for it:
483
+
484
+ 1. Start from `EgressPolicy.denyAll()`. A host that no rule allows is refused.
485
+ 2. Add `applyPreset(egressPreset.softwareDevelopment())`: package and container registries, and reads
486
+ from common services, with no key.
487
+ 3. Keep `routeModelsViaGateway(...)` or the model key's rule, and every token or connection rule.
488
+ 4. Add one `allow` rule for each other host the agents reach, with `priority: 100` and the methods it needs.
489
+
490
+ Prove the closed list on Orchestra's cloud, not locally: a local run enforces no egress rule ("On this
491
+ computer" above). Apply the tightened policy, then ask an agent to run the build and the tests once on a cloud
492
+ machine. A refused host shows as `destination is not allowed` (`troubleshooting.md`).
361
493
 
362
494
  #### Setup script or a baked image
363
495
 
@@ -392,27 +524,152 @@ Choose B when the code needs a particular kernel or operating system.
392
524
 
393
525
  - **Windows:** possible. Set `platform: { os: "windows", arch: "amd64", runtime: "vm" }` on the
394
526
  environment. A Windows machine is a full EC2 virtual machine with 4 or 8 CPU. Docker data disks are not
395
- available on Windows, so remove `disks` and the Docker setup. The Linux `setup.sh` cannot run there. Ask the Orchestra team
396
- how a setup script runs on Windows before you rely on one.
527
+ available on Windows, so remove `disks` and the Docker setup. The Linux `setup.sh` cannot run there: write
528
+ a PowerShell script from "A Windows setup script" below. Follow "Windows in three stages" in section 5.
397
529
  - **Linux on a full VM:** possible with `runtime: "vm"`, at 2, 4 or 8 CPU.
398
530
  - **macOS:** only as the team's own Mac, enrolled with the worker. Say what that needs before you offer it
399
531
  (the orchestra skill, section 18), and agree it with the Orchestra team first.
400
532
 
401
- ### C. The team's private network
402
-
403
- Choose C when development needs services or data inside their VPN or their own cloud.
404
-
405
- Orchestra's machines cannot reach a private network today. The planned answer is the *Launcher*, which
406
- runs machines inside the team's own cloud; it does not exist yet, and there is no Azure support. Tell the
407
- developer:
408
-
409
- - what their code needs from the private network, host by host;
410
- - that this needs a later Orchestra release, or one of their Macs or Linux servers enrolled as a machine
411
- (the orchestra skill, section 18);
412
- - that they can still see a first fix today on the todo sample, or on the parts of the code that need no
413
- private service.
414
-
415
- Write down what is missing and stop here. Report it to the developer, and go back to the orchestra-fdk skill.
533
+ ### A Windows setup script
534
+
535
+ Tried by us: not yet.
536
+
537
+ A Windows machine runs the setup script with PowerShell 5.1 (`powershell -NonInteractive -NoProfile
538
+ -ExecutionPolicy Bypass -File setup.ps1`), as the worker's service account, once per machine. A parked VM that
539
+ starts again does not run it again. A new VM does, for example after `ttl` deletes the old one. So write each
540
+ step so that it can run again: check before you install, as the template does. Start from this template. Keep the steps in this order, and give each one its own exit code. The work folder is
541
+ `C:\work\code`. The setup script does not clone the code. It runs before any code host key exists, so a
542
+ clone of a private repository fails, and the machine start fails with it. The agent clones the code in its
543
+ turn, from a git bundle or a public address (`phases/5-prove.md`, section 1).
544
+
545
+ ```powershell
546
+ $ErrorActionPreference = 'Stop'
547
+ $ProgressPreference = 'SilentlyContinue'
548
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
549
+
550
+ # 10: the egress edge's certificate authority is trusted machine-wide. The worker hands the script its path.
551
+ $ca = $env:GIT_SSL_CAINFO
552
+ if ($ca) {
553
+ try {
554
+ $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($ca)
555
+ if (-not (Test-Path "Cert:\LocalMachine\Root\$($cert.Thumbprint)")) {
556
+ Import-Certificate -FilePath $ca -CertStoreLocation Cert:\LocalMachine\Root | Out-Null
557
+ }
558
+ } catch { Write-Output "trusting the edge authority failed: $_"; exit 10 }
559
+ }
560
+
561
+ # 11: the work folder, for the service account and the administrators only.
562
+ try { New-Item -ItemType Directory -Force -Path C:\work | Out-Null } catch { exit 11 }
563
+ icacls C:\work /inheritance:r /grant:r '*S-1-5-18:(OI)(CI)F' '*S-1-5-32-544:(OI)(CI)F' | Out-Null
564
+ if ($LASTEXITCODE -ne 0) { exit 11 }
565
+
566
+ # 12: Git for Windows. Set the version the team uses.
567
+ $git = 'C:\Program Files\Git\cmd\git.exe'
568
+ if (-not (Test-Path $git)) {
569
+ $version = '<version, such as 2.47.1>'
570
+ $installer = 'C:\work\git-setup.exe'
571
+ try {
572
+ Invoke-WebRequest -UseBasicParsing -OutFile $installer `
573
+ -Uri "https://github.com/git-for-windows/git/releases/download/v$version.windows.1/Git-$version-64-bit.exe"
574
+ } catch { exit 12 }
575
+ $run = Start-Process -FilePath $installer -ArgumentList '/VERYSILENT', '/NORESTART', '/NOCANCEL', '/SP-' -Wait -PassThru
576
+ if ($run.ExitCode -ne 0) { exit 12 }
577
+ }
578
+ # For the rest of this script only. The agent does not get this PATH.
579
+ $env:Path = "C:\Program Files\Git\cmd;$env:Path"
580
+
581
+ # 13: git trusts the Windows certificate store, for every process on the machine.
582
+ & $git config --system http.sslBackend schannel
583
+ if ($LASTEXITCODE -ne 0) { exit 13 }
584
+
585
+ # 14 and on: the team's toolchain that fits in the time limit, one exit code per step.
586
+ ```
587
+
588
+ Why each step is there:
589
+
590
+ - `$ErrorActionPreference = 'Stop'` stops on a failing PowerShell command, but not on a failing program such as
591
+ `icacls` or `git`. So each program call is followed by a `$LASTEXITCODE` check that exits non-zero.
592
+ - The worker already imports the edge's authority into `Cert:\LocalMachine\Root` before the script runs, and
593
+ logs a failure without stopping. Step 10 checks it, and imports it when it is missing. The worker sets each
594
+ certificate variable a harness reads, `GIT_SSL_CAINFO` among them, to the authority's file.
595
+ - PowerShell 5.1 does not always offer TLS 1.2, so a download can fail before it reaches the edge.
596
+ - The Windows image has no git.
597
+ - The agent does not find git on its PATH. Its PATH is the worker's, fixed when the worker started, before
598
+ the setup script ran. The installer's change to the machine PATH and the script's `$env:Path` do not
599
+ reach it. So the agent calls git by its full path, `C:\Program Files\Git\cmd\git.exe`. Write this in the
600
+ agent's instructions or in the codebase skill.
601
+ - Git for Windows reads its own certificate bundle, not the Windows store. The worker sets `GIT_SSL_CAINFO` for
602
+ the setup script and the harness only, so git run any other way fails on the edge's certificate. The
603
+ `schannel` setting makes every git on the machine read the Windows store.
604
+ - The SIDs `S-1-5-18` (the service account, LocalSystem) and `S-1-5-32-544` (the administrators) work on every
605
+ language of Windows, where the account names do not. On Windows the agent runs as the worker's account.
606
+ - The setup script has 10 minutes, and the limit is fixed for now. A toolchain such as Visual Studio Build
607
+ Tools can take 10 to 20 minutes to install. Leave such an install to the agent's first turn: a Windows
608
+ machine is parked when idle and keeps its disk, so it stays installed. The agent waits for the install in
609
+ the turn: a park kills an install that is still running. A process the setup script leaves running is
610
+ killed the same way. For machines that start with it
611
+ installed, use the team's own image ("Windows in three stages", stage 2).
612
+ - The output is kept as it is. Never print a token.
613
+
614
+ ### C. Where the machines run, when the code needs private services
615
+
616
+ Choose here when development needs a database, internal APIs or a package feed inside the team's network
617
+ or cloud. List what the code needs, host by host, before you choose.
618
+
619
+ | | Hosted | A compute provider in their cloud | Their own machine | The whole platform in their cloud |
620
+ |---|---|---|---|---|
621
+ | Benefit | The simplest start. Nothing to run. Keys added at our network edge, machine endpoints and views, machines kept ready, and we keep it running. | Keeps their setup, networks, images and private services inside their own cloud, under their own account and controls. | Their own machine or network, as it is. | Nothing leaves their cloud. |
622
+ | What reaches what | Our machines reach their services over the internet, through the network edge | Their Launcher asks our platform for orders over HTTPS. Their machines connect out to our platform. Nothing connects in. | The worker on their machine connects out to our platform | All inside their cloud |
623
+ | Where keys live | Secrets, added at our network edge | On their machines, set by their own boot step. No network edge of ours stands in front of their machines yet | On their machine | In their cloud |
624
+ | Where files rest | Our object store (S3) | Not stated yet | Not stated yet | Their cloud |
625
+ | Setup | None | Make a compute provider record, deploy the Launcher template to their Azure subscription, name the record in `provisionSpec.computeProviderId` | Enroll the machine (`orchestra worker enroll`) | An installation of the platform's Helm chart |
626
+ | Status | Released | Released on Azure. Not released yet: AWS | Released | Not released yet |
627
+
628
+ Pick hosted when the private services can be reached over the internet with a key.
629
+ Pick a compute provider when the services must stay inside their cloud, and they use Azure.
630
+ Pick their own machine when the code needs one machine they already run, such as a Mac.
631
+
632
+ Tell the developer the benefit of each choice first. The limits are for troubleshooting. Do not lead with
633
+ them when you present the choice.
634
+
635
+ Cannot do:
636
+
637
+ - Hosted: reach a service that is not on the internet.
638
+ - A compute provider: run on AWS or Google Cloud; run on arm64; keep `/home` on a Kata pod target; add
639
+ keys at our network edge.
640
+
641
+ Tokens on a compute provider's machines:
642
+
643
+ - The team manages every credential on their own machines. The platform delivers none there.
644
+ - The model sign-in goes in the worker service's own environment, by harness:
645
+ - `claude` with a Claude subscription token: `ORCHESTRA_WORKER_CLAUDE_OAUTH_TOKEN`.
646
+ - `claude` with an Anthropic API key or an Orchestra gateway key: `ANTHROPIC_API_KEY`. This value
647
+ replaces the placeholder that our image sets. With a gateway key, keep the agent's model gateway on:
648
+ the platform sets the gateway address.
649
+ - `codex`: `ORCHESTRA_WORKER_OPENAI_API_KEY`, or `ORCHESTRA_WORKER_CODEX_PERSONAL_ACCESS_TOKEN`.
650
+ - On Windows, the worker service is `SfdkWorker`, and its environment is the service's `Environment`
651
+ registry value.
652
+ - Other tokens, such as for git, a package feed or an Azure DevOps personal access token: the team's own
653
+ boot step sets them. For example, the step reads their Key Vault with a managed identity on the VM.
654
+ - The worker reads its environment when its service starts. So the boot step runs before the worker
655
+ starts, or restarts the worker service after it sets the values. On our Windows image, the startup task
656
+ `orchestra-azure-userdata` starts `SfdkWorker` at each boot.
657
+ - Do not put a token in the environment's `env` (`provisionSpec.env`). The platform stores it, an
658
+ environment read returns it, and it travels in plain text in the VM's startup data.
659
+ - The Launcher attaches no managed identity to the VMs it creates. The team's automation finds the VMs by
660
+ two tags: `orchestra-launcher` holds the compute provider record's id, and `orchestra-machine` holds
661
+ the machine's id. Not released yet: a managed identity attached when the Launcher creates a VM.
662
+ - Never bake a credential into an image: that is not supported.
663
+ - Not released yet: our network edge for machines in the team's own network, which adds keys from
664
+ Secrets as it does for hosted machines.
665
+
666
+ The Launcher's VM targets use the `Dsv5` size family by default. A subscription with no `Dsv5` capacity
667
+ sets `vmSizeFamily: Dsv6` on each VM target in the Launcher's settings.
668
+ - Their own machine: hold network rules; add keys at the edge; start a machine on demand.
669
+
670
+ When no choice fits, write down what is missing and stop here. Report it to the developer, and go back to
671
+ the orchestra-fdk skill. They can still see a first fix on the todo sample, or on the parts of the code
672
+ that need no private service.
416
673
 
417
674
  ## 6. Write and check
418
675
 
@@ -421,38 +678,89 @@ Write down what is missing and stop here. Report it to the developer, and go bac
421
678
  2. In `<factory>`, run `npm run typecheck`, then `npx orchestra fdk synth`. The starter is one program, at the folder's
422
679
  root.
423
680
  3. Read `<factory>/fdk.out/plan.json`. Every environment has your setup script, ports and settings, and
424
- the model rule from step 6 of the orchestra-fdk skill: the `model-gateway` rule,
425
- or a Secret's rule on the provider's host. On a new factory, no environment has a Bitbucket or Jira rule
426
- yet: those come with the tokens, in step 12 of the orchestra-fdk skill. On an existing factory, with its
427
- inputs loaded, the rules it has now stay as they are.
681
+ the model rule of the orchestra-fdk skill's phase 2: the `model-gateway` rule, or a Secret's rule on the
682
+ provider's host. On a new factory, no environment has a code host or tracker rule yet: phase 3 adds them
683
+ after the first run. On an existing factory, with its token file loaded, the rules it has now stay as
684
+ they are.
428
685
  4. Write `ENVIRONMENT.md` in `<factory>`: what you found and where, the mode you proved in and its result
429
686
  for each success criterion, the commands, every outbound attempt the proof logged, and what is still
430
687
  unknown.
431
688
 
432
689
  **Check:** the typecheck and the synth pass, and the plan is as in item 3. Go back to the orchestra-fdk
433
- step that sent you for the apply.
690
+ skill's phase 4 for the apply.
434
691
 
435
692
  ## 7. The cloud
436
693
 
437
694
  The cloud is the fallback when no local mode can run. It is also the confirmation after the local proof:
438
- the Manager Agent's "Set up" item, which step 10 of the orchestra-fdk skill starts. It proves what only the
439
- cloud can: the micro VM, Docker, the network rules and the tokens. The Engineer Agents' machines cannot clone from
440
- Bitbucket, so the Set up item also proves the tests run from a git bundle.
695
+ the Set up item, which phase 5 of the orchestra-fdk skill starts. It proves what only the cloud can: the
696
+ micro VM, Docker, the network edge and the machine endpoints. The Engineer Agents start each item from a git
697
+ bundle, so Set up also proves the tests run from one.
441
698
 
442
699
  It is slower. Each try is an apply plus a machine start, and access problems show one at a time.
443
700
 
444
- To prove in the cloud, write the environment as in sections 5 and 6, apply it, and run Set up with the git
445
- bundle. Keep the safety rule:
446
-
447
- 1. For the proof, set the environment's default action to `deny`. Allow only the package registries and
448
- image registries the install uses, and keep the `model-gateway` rule. Calls to the Orchestra platform
449
- skip these rules.
450
- 2. Use only local settings in the Set up item's instructions.
451
- 3. Ask the Manager Agent to report every refused host, and to note any production host it sees and
452
- continue.
453
-
454
- When a step fails, fix it here, prove the fix locally if a local mode runs, apply again, and assign the
455
- item again.
701
+ To prove in the cloud, write the environment as in sections 5 and 6, apply it, and run Set up. Egress stays
702
+ open. Keep the safety rule this way:
703
+
704
+ 1. Use only local settings in the Set up item's description.
705
+ 2. Ask the Manager Agent to note any production host it sees in a setting or a log, and to continue.
706
+
707
+ When a step fails, fix it here, prove the fix locally if a local mode runs, apply again, and assign Set up
708
+ again. `troubleshooting.md` names each failure.
709
+
710
+ ### The model in the cloud: subscription, model gateway, or own key via the vault
711
+
712
+ The cloud agents reach a model in one of three ways. Ask the developer which one, and walk them through
713
+ its steps once. They copy every value themselves; you never read, print or copy it.
714
+
715
+ 1. **Subscription: their own Claude or Codex sign-in.** Follow the sign-in steps below.
716
+ 2. **Model gateway: Orchestra provides the key.** One endpoint reaches OpenAI, Anthropic and open-source
717
+ models, on the organization's credits. They can also bring their own provider key to the gateway, with
718
+ `orchestra fdk init --model-key anthropic` or `openai`. The gateway also routes models:
719
+ `orchestra model-configs` sets which providers serve a model and in what order, so a call falls back to
720
+ the next one when a provider is down, and `orchestra model-aliases` gives a model a name of their own, such as `default`.
721
+ 3. **Own API key via the vault.** Their Anthropic or OpenAI API key is a platform Secret, added at the
722
+ network edge on calls straight to `api.anthropic.com` or `api.openai.com`, with no gateway in between
723
+ and with the agent's gateway setting off. Nothing to sync, because an API key does not renew. When they
724
+ have an API key, recommend this over a subscription: no file to copy and no shared `/home`.
725
+ `references/authoring.md` § How the agents reach a model shows the declaration.
726
+
727
+ The sign-in steps. The developer makes the sign-in in a real terminal on their computer; run from an
728
+ agent's shell, its browser step hangs.
729
+
730
+ - **Claude: a long-lived token in a Secret, the recommended way.** `claude setup-token` prints an
731
+ `sk-ant-oat…` token made for servers. It lasts about a year, and nothing on a machine renews or revokes
732
+ it, so it needs no shared `/home`. Save it to a file only the developer can read, never through a chat:
733
+ copy it, then `umask 077 && pbpaste | tr -d '\n' > <file> && pbcopy </dev/null`. At apply, the shell
734
+ exports it from that file into the variable a `Secret` reads. An egress rule allows `api.anthropic.com`
735
+ and adds `Authorization: Bearer ${secret}`, removing `x-api-key`. The environment's `env` holds only
736
+ `CLAUDE_CODE_OAUTH_TOKEN=software-factory-egress-placeholder` and an empty `ANTHROPIC_API_KEY`, and the
737
+ agent's gateway setting is off. Never put the token itself in `env`. The Claude sign-in recipe in
738
+ `references/authoring.md` shows the declaration. Copying the renewing credentials file is the fallback,
739
+ and follows the ChatGPT sign-in steps below.
740
+ - **Codex: a personal access token, where the workspace allows one.** A ChatGPT Business or Enterprise
741
+ member creates it at chatgpt.com/admin/access-tokens, once the workspace owner allows access tokens,
742
+ and chooses how long it lasts, such as 7 to 90 days. Codex never renews it, so it needs no shared
743
+ `/home`; the developer supplies a new one before it expires. It begins `at-` and goes in
744
+ `ORCHESTRA_WORKER_CODEX_PERSONAL_ACCESS_TOKEN`, which outranks a ChatGPT sign-in or an API key beside
745
+ it. On their own computer, `codex login --with-access-token` reads the same token from standard input.
746
+ - **Codex otherwise: a separate ChatGPT sign-in on a shared `/home`.** The other long-lived choice is an
747
+ OpenAI API key, billed by use rather than by the subscription (option 3).
748
+ 1. **Make a separate sign-in, never a copy of the local one:** `mkdir -p <dir> && CODEX_HOME=<dir> codex
749
+ login` (`--device-auth` only with no browser). Its tokens in `<dir>/auth.json` go in the
750
+ `ORCHESTRA_WORKER_CODEX_` variables, or a setup step writes the file.
751
+ 2. **Give the environment a durable `/home` volume,** shared by every machine that uses the sign-in, so
752
+ one file renews for all of them. Each renewal replaces the refresh token, so separate copies sign
753
+ each other out; two machines that renew in the same second can too, and then need a new sign-in.
754
+ Remove the variables after the first machine starts.
755
+ 3. **Never sign in again in the default folder after the copy.** A new `codex login` in `~/.codex`
756
+ revokes the cloud copy, and every machine answers "refresh token was revoked". If it happened, make
757
+ a new separate sign-in.
758
+ - **Keep a Codex sign-in off an agent that uses the model gateway.** With a ChatGPT sign-in or a personal
759
+ access token, Codex compresses its requests, and the gateway refuses them with `model_not_found`, a
760
+ known issue. Give such an agent the subscription directly, with its gateway setting off, or keep the
761
+ homes apart.
762
+
763
+ The sign-in values are plain text: the environment's settings are stored unencrypted and shown on every read.
456
764
 
457
765
  ## What the platform can do
458
766
 
@@ -460,8 +768,9 @@ Use the orchestra skill to build an answer for a case the paths do not cover. Ne
460
768
  yet" lines. The sections this work needs:
461
769
 
462
770
  - Section 4: environments, machines, sizes, images, setup scripts, disks, Windows and full VMs.
463
- - Section 5: network rules, their live effect on running machines, and commissioning.
771
+ - Section 5: network rules, and their live effect on running machines.
464
772
  - Section 6: secrets, and where their values come from.
465
773
  - Section 7: the model gateway rule every environment needs.
466
- - Section 17: connections, the later option for the code host and the tracker.
774
+ - Section 17: connections. The files under `references/integrations/` say how to give access to each
775
+ service.
467
776
  - Section 18: the team's own machines, the local environment, and private networks.