Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.vscode/
.opencode/

config.pkl

Expand Down
5 changes: 5 additions & 0 deletions .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ description = "Render tfvars and show what would change"
dir = "{{ config_root }}/ansible"
run = "ansible-playbook deploy.yml --tags=render,init,plan"

[tasks.undeploy-mobile]
description = "Tear down constructor-mobile Terraform resources"
dir = "{{ config_root }}/ansible"
run = "ansible-playbook undeploy-mobile.yml"

[settings]
lockfile = true
experimental = true
5 changes: 5 additions & 0 deletions ansible/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
loop_control:
loop_var: project
label: "{{ project.name }}"
when: project.name != mobile_project or (mobile_enabled | bool)
tags: [sync, upstream]

- import_tasks: tasks/03-bootstrap-ba.yml
tags: [bootstrap, build, ba]

- import_tasks: tasks/03b-bootstrap-mobile.yml
when: mobile_enabled | bool
tags: [bootstrap, build, mobile]
Comment on lines 26 to 31

# background-agents must be initialized/applied before constructor-mobile.
Expand All @@ -43,10 +45,13 @@
# constructor-mobile consumes the BA control-plane URL, so keep all mobile
# Terraform work after BA readiness is verified.
- import_tasks: tasks/06-capture-ba-outputs.yml
when: mobile_enabled | bool
tags: [mobile, capture, terraform]

- import_tasks: tasks/06a-terraform-init-mobile.yml
when: mobile_enabled | bool
tags: [mobile, init, terraform]

- import_tasks: tasks/06b-terraform-apply-mobile.yml
when: mobile_enabled | bool
tags: [mobile, apply]
1 change: 1 addition & 0 deletions ansible/group_vars/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ config_render_root: "{{ config_root }}/render"
deploy_dir: "{{ repo_root }}/deploy"
inputs_json: "{{ deploy_dir }}/inputs.json"
mobile_inputs_json: "{{ deploy_dir }}/mobile-inputs.json"
mobile_enabled: false
state_json: "{{ deploy_dir }}/state.json"
projects_json: "{{ deploy_dir }}/projects.json"

Expand Down
8 changes: 8 additions & 0 deletions ansible/tasks/01-render-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@
loop_control:
label: "{{ item.dst | basename }}"
changed_when: true

- name: Load rendered mobile settings
ansible.builtin.set_fact:
mobile_config: "{{ lookup('file', mobile_inputs_json) | from_json }}"

- name: Set mobile provisioning flag
ansible.builtin.set_fact:
mobile_enabled: "{{ mobile_config.enabled | default(false) | bool }}"
2 changes: 1 addition & 1 deletion ansible/tasks/04-terraform-init-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
dest: "{{ terraform_stack_tf_dir }}/{{ terraform_var_file }}"
mode: "0600"
content: |
{% for key, value in (terraform_inputs | combine(terraform_stack_extra_tfvars | default({}))).items() %}
{% for key, value in (terraform_inputs | dict2items | rejectattr('key', 'equalto', 'enabled') | items2dict | combine(terraform_stack_extra_tfvars | default({}))).items() %}
{% if value is string and '\n' in value %}
{{ key }} = <<-EOF
{{ value }}
Expand Down
15 changes: 2 additions & 13 deletions ansible/tasks/06-capture-ba-outputs.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
---
- name: Load b-a inputs (for deterministic URL construction)
ansible.builtin.set_fact:
ba_inputs: "{{ lookup('file', inputs_json) | from_json }}"
tf_state: "{{ lookup('file', state_json) | from_json }}"
- name: Compute b-a URLs
ansible.builtin.import_tasks: 06-compute-ba-urls.yml

- name: Verify background-agents has been deployed
ansible.builtin.command:
Expand All @@ -19,15 +17,6 @@
'cloudflare_worker' not in ba_state_list.stdout or
'cloudflare_workers_deployment' not in ba_state_list.stdout

- name: Compute b-a control plane host
ansible.builtin.set_fact:
ba_control_plane_host: "open-inspect-control-plane-{{ ba_inputs.deployment_name }}.{{ ba_inputs.cloudflare_worker_subdomain }}.workers.dev"

- name: Derive b-a URLs from host
ansible.builtin.set_fact:
ba_control_plane_url: "https://{{ ba_control_plane_host }}"
ba_ws_url: "wss://{{ ba_control_plane_host }}"

- name: Confirm captured values
ansible.builtin.debug:
msg:
Expand Down
14 changes: 14 additions & 0 deletions ansible/tasks/06-compute-ba-urls.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
- name: Load b-a inputs and state
ansible.builtin.set_fact:
ba_inputs: "{{ lookup('file', inputs_json) | from_json }}"
tf_state: "{{ lookup('file', state_json) | from_json }}"

- name: Compute b-a control plane host
ansible.builtin.set_fact:
ba_control_plane_host: "open-inspect-control-plane-{{ ba_inputs.deployment_name }}.{{ ba_inputs.cloudflare_worker_subdomain }}.workers.dev"

- name: Derive b-a URLs from host
ansible.builtin.set_fact:
ba_control_plane_url: "https://{{ ba_control_plane_host }}"
ba_ws_url: "wss://{{ ba_control_plane_host }}"
19 changes: 19 additions & 0 deletions ansible/tasks/07-terraform-destroy-mobile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
- name: Define mobile terraform environment
ansible.builtin.set_fact:
mobile_tf_env:
AWS_ACCESS_KEY_ID: "{{ tf_state.access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ tf_state.secret_key }}"
AWS_REGION: "{{ tf_state.region }}"

- name: Terraform destroy (mobile)
ansible.builtin.command:
cmd: "terraform destroy {{ terraform_apply_args }}"
chdir: "{{ mobile_tf_dir }}"
environment: "{{ mobile_tf_env }}"
register: mobile_destroy
changed_when: "'Destroy complete' in mobile_destroy.stdout"

- name: Destroy summary
ansible.builtin.debug:
msg: "constructor-mobile teardown finished"
32 changes: 32 additions & 0 deletions ansible/undeploy-mobile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
- name: Undeploy constructor-mobile stack
hosts: local
connection: local
gather_facts: false

tasks:
- import_tasks: tasks/00-preflight.yml
tags: [preflight]

- import_tasks: tasks/01-render-config.yml
tags: [render, config]

- name: Sync constructor-mobile project
ansible.builtin.include_tasks:
file: tasks/02-sync-upstream.yml
apply:
tags: [sync, upstream, mobile]
loop: "{{ projects | selectattr('name', 'equalto', mobile_project) }}"
loop_control:
loop_var: project
label: "{{ project.name }}"
tags: [sync, upstream, mobile]

- import_tasks: tasks/06-compute-ba-urls.yml
tags: [mobile, terraform]

- import_tasks: tasks/06a-terraform-init-mobile.yml
tags: [mobile, init, terraform]

- import_tasks: tasks/07-terraform-destroy-mobile.yml
tags: [mobile, destroy]
6 changes: 5 additions & 1 deletion config.example.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,16 @@ deployment {
}

mobile {
// Set true to provision constructor-mobile during `mise run deploy`.
enabled = false

// Defaults are fine; uncomment to customize
// nameSuffix = "prod"
// customDomain = "gateway.example.com"
// pushCron = new Listing { "*/2 * * * *" }

appJwtSigningKey = "REPLACE_ME_base64_from_openssl_rand_base64_32" // openssl rand -base64 32
// Required only when enabled = true.
// appJwtSigningKey = "REPLACE_ME_base64_from_openssl_rand_base64_32" // openssl rand -base64 32
}

// ─── Optional per-project overrides ───────────────────────────────────
Expand Down
6 changes: 5 additions & 1 deletion config/render/mobile-inputs.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import "../../config.pkl" as c
local m = c.mobile
local d = c.deployment

local defaultAppJwtSigningKey = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="

local baseFields = new Mapping {
["enabled"] = m.enabled

// Cloudflare (reused from b-a's config)
["cloudflare_account_id"] = d.cloudflare.accountId
["cloudflare_api_token"] = d.cloudflare.apiToken
Expand All @@ -30,7 +34,7 @@ local baseFields = new Mapping {
["github_oauth_client_secret"] = d.github.app.clientSecret

// Mobile-only secret
["app_jwt_signing_key"] = m.appJwtSigningKey
["app_jwt_signing_key"] = if (m.appJwtSigningKey != null) m.appJwtSigningKey else defaultAppJwtSigningKey

// Shared secret: must match b-a's value
["internal_callback_secret"] = d.security.internalCallbackSecret
Expand Down
8 changes: 7 additions & 1 deletion config/schema/Root.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import "../defaults/projects.pkl" as ProjectDefaults
projects: Mapping<String, Project.Project> = ProjectDefaults.projects

deployment: Deployment.Deployment
mobile: Mobile.Mobile
mobile: Mobile.Mobile = new {}
state: State.State

local sandboxError: String? =
Expand Down Expand Up @@ -43,6 +43,11 @@ local githubBotError: String? =
"github.bot is enabled but webhookSecret or username is missing"
else null

local mobileError: String? =
if (mobile.enabled && mobile.appJwtSigningKey == null)
"mobile is enabled but appJwtSigningKey is missing"
else null

hidden unsupportedTweakcnThemeProjects: Listing<String> = new Listing {
for (key, value in projects.toMap()) {
when (key != "background-agents" && value.tweakcnThemeUrl != null) {
Expand All @@ -63,6 +68,7 @@ hidden _errors: List<String?> = List(
webError,
accessControlError,
githubBotError,
mobileError,
tweakcnThemeError
)

Expand Down
5 changes: 4 additions & 1 deletion config/schema/mobile/Mobile.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ typealias Base64_32 = String(
)

class Mobile {
/// Set true to provision constructor-mobile during deploy.
enabled: Boolean = false

/// Suffix for resource names (e.g. "prod", "staging")
nameSuffix: String(length > 0) = "prod"

Expand All @@ -23,5 +26,5 @@ class Mobile {

/// Gateway-owned key used to sign short-lived app-session JWTs.
/// Generate: openssl rand -base64 32
appJwtSigningKey: Base64_32
appJwtSigningKey: Base64_32? = null
}