Compare commits
188 Commits
feature/as
...
150cef1aca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
150cef1aca | ||
|
|
a30ad99ee4 | ||
|
|
d2b6d95a49 | ||
|
|
adc415e95a | ||
|
|
e8303d5129 | ||
|
|
f37021346b | ||
|
|
5a99928c6f | ||
|
|
59c83c296b | ||
|
|
b6cb031edb | ||
|
|
cb5ffc16d8 | ||
|
|
f375c9567f | ||
|
|
f0400c02b6 | ||
|
|
d1d7331238 | ||
|
|
459dbc5d18 | ||
| a4a68eeb5a | |||
|
|
99958979d6 | ||
|
|
0e4d229df2 | ||
|
|
53883108d8 | ||
|
|
fcbf6ce092 | ||
|
|
cf21863b0f | ||
|
|
653a923fa8 | ||
|
|
13c819e66c | ||
|
|
28a653b203 | ||
|
|
cf7ab7fbe6 | ||
|
|
12d0a75b3a | ||
|
|
668e86d7c2 | ||
| 0fb593a313 | |||
|
|
b5dc130207 | ||
|
|
0efa1e5125 | ||
|
|
34e05725dd | ||
|
|
b6b1bee25c | ||
|
|
3b3461fd3c | ||
|
|
7cbed63c92 | ||
|
|
a008e766f2 | ||
|
|
543394a825 | ||
|
|
23d6d75117 | ||
|
|
86433a58d0 | ||
|
|
3344e24a48 | ||
|
|
2621bc9f36 | ||
|
|
72de87c8c4 | ||
| 0ef9703757 | |||
|
|
d77d213d89 | ||
|
|
e793794fdd | ||
|
|
d1ae5ba7a0 | ||
|
|
84e30c8ee2 | ||
|
|
644128cd3f | ||
|
|
6912f5c55d | ||
|
|
fc0e39b9c7 | ||
|
|
8627b00ed8 | ||
|
|
0212f0fdd2 | ||
|
|
edfe594e7e | ||
|
|
791f13fca5 | ||
|
|
c3248fde1f | ||
|
|
c137ea0881 | ||
|
|
818b6505dd | ||
|
|
4a1958876f | ||
|
|
6bdb536848 | ||
|
|
6023ee25e1 | ||
|
|
e5d24f557a | ||
|
|
f8e137b67b | ||
|
|
76241e75a8 | ||
|
|
d5ce6ff96a | ||
|
|
44b1a2fb33 | ||
|
|
7dc1999928 | ||
|
|
63d480927d | ||
|
|
fa86fa4c9c | ||
|
|
444b597ade | ||
|
|
c81a9b7704 | ||
|
|
6cab6519b1 | ||
|
|
ce992ca743 | ||
|
|
092d1ac209 | ||
|
|
6acf2f9944 | ||
|
|
8d18f42b2e | ||
|
|
152f10ed8b | ||
|
|
d99ebca829 | ||
| df91305e13 | |||
|
|
6f2b6e0290 | ||
|
|
fc34833472 | ||
|
|
7f37211a8b | ||
|
|
ccc956f70b | ||
|
|
511f32e521 | ||
|
|
87a3e84f5b | ||
|
|
1743145e9f | ||
|
|
d561ac6e04 | ||
|
|
99bc31dee9 | ||
|
|
ac8e7acbd4 | ||
|
|
0ad5dbe741 | ||
|
|
7d9b054340 | ||
|
|
4b1e8a7cac | ||
|
|
8f190eb188 | ||
|
|
95ae6919b0 | ||
|
|
52e97f3a7c | ||
|
|
461aa1bc54 | ||
|
|
c38461a6e8 | ||
|
|
6bcb6fa93f | ||
|
|
f4181349f8 | ||
|
|
9d860367cc | ||
|
|
f654c59dd5 | ||
|
|
d22ac5cef2 | ||
|
|
0fd69e0b90 | ||
|
|
e8d87ff092 | ||
|
|
23612a38f2 | ||
|
|
bd100c15e7 | ||
|
|
dc5392446c | ||
|
|
f8cf139b10 | ||
|
|
ac955d327f | ||
|
|
9e6339037a | ||
|
|
b647f6afee | ||
|
|
aabb3d5009 | ||
|
|
4ed64ab91c | ||
|
|
f57e0bef02 | ||
|
|
9ed7466fd8 | ||
|
|
4d7766d1b1 | ||
|
|
09ae954085 | ||
|
|
8fd9fd5b20 | ||
|
|
dcb764eca7 | ||
|
|
b6a4ad6816 | ||
|
|
bbaaf655fa | ||
|
|
7228dc6e11 | ||
|
|
8953702608 | ||
|
|
0be33cb8db | ||
|
|
0f0b5db29b | ||
|
|
009f244739 | ||
|
|
2f87039f17 | ||
|
|
72fa38e928 | ||
|
|
9e68802090 | ||
|
|
d05cfcf317 | ||
|
|
80f810fb0c | ||
|
|
9153324795 | ||
|
|
91b5817e5f | ||
|
|
1dfa7889ab | ||
|
|
08d7da0c35 | ||
|
|
9ebeb42023 | ||
|
|
075f34b1fb | ||
|
|
210c89c2c7 | ||
|
|
b93a6e50ab | ||
|
|
85cc1f8c6a | ||
|
|
8e781b0c54 | ||
|
|
d8ad35b8e9 | ||
|
|
a9973d1e0f | ||
| 4113011f63 | |||
|
|
018782d986 | ||
|
|
3681e8c03e | ||
|
|
8f377e4cf3 | ||
|
|
419acaa40d | ||
|
|
42b204bf8a | ||
|
|
1d7dcb7d82 | ||
|
|
d63ca0b4f9 | ||
|
|
e9440327aa | ||
|
|
dcc7e282c7 | ||
|
|
6d5fc7c5c6 | ||
| 312fdf9986 | |||
|
|
4e0b4fa049 | ||
|
|
cf7c2a1436 | ||
|
|
3dc6555ad1 | ||
|
|
27ff9286b4 | ||
|
|
6e50461999 | ||
|
|
3f1c3a40cf | ||
|
|
0116ec4cc3 | ||
|
|
f962d0a6d7 | ||
|
|
dc3c0d7cb1 | ||
|
|
b05f9fad09 | ||
|
|
8650995926 | ||
|
|
e5469d2cb8 | ||
|
|
702698ddcd | ||
|
|
d233d582d4 | ||
|
|
3839fac162 | ||
|
|
b01dac85da | ||
|
|
37a49824d0 | ||
|
|
0127016ab2 | ||
|
|
e0b6fcb24a | ||
|
|
e7d9a8fec5 | ||
|
|
1a9addc537 | ||
|
|
401f25b1c4 | ||
|
|
ece522074e | ||
|
|
c30c0074f1 | ||
|
|
fa51dc2c4d | ||
|
|
c1810fde8a | ||
| a781ef8b14 | |||
|
|
92b2a9d609 | ||
|
|
9250b0f193 | ||
| 24869f47ee | |||
| bb5a57e909 | |||
| 9f3d81729d | |||
| 064d3e8b3d | |||
| ef7e3c61ed | |||
| 64951e1e5e | |||
| 58931732f7 |
@@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
# (pathspec) Colon-separated paths in which Ansible will search for collections content. Collections must be in nested *subdirectories*, not directly in these directories. For example, if ``COLLECTIONS_PATHS`` includes ``'{{ ANSIBLE_HOME ~ "/collections" }}'``, and you want to add ``my.collection`` to that directory, it must be saved as ``'{{ ANSIBLE_HOME} ~ "/collections/ansible_collections/my/collection" }}'``.
|
# (pathspec) Colon-separated paths in which Ansible will search for collections content. Collections must be in nested *subdirectories*, not directly in these directories. For example, if ``COLLECTIONS_PATHS`` includes ``'{{ ANSIBLE_HOME ~ "/collections" }}'``, and you want to add ``my.collection`` to that directory, it must be saved as ``'{{ ANSIBLE_HOME} ~ "/collections/ansible_collections/my/collection" }}'``.
|
||||||
|
|
||||||
;collections_path=/Users/rblundon/.ansible/collections:/usr/share/ansible/collections
|
collections_path=/opt/ansible-collections:/usr/share/ansible/collections
|
||||||
|
|
||||||
# (boolean) A boolean to enable or disable scanning the sys.path for installed collections.
|
# (boolean) A boolean to enable or disable scanning the sys.path for installed collections.
|
||||||
;collections_scan_sys_path=True
|
;collections_scan_sys_path=True
|
||||||
@@ -209,7 +209,7 @@ private_key_file=~/.ssh/ansible
|
|||||||
remote_user=wed
|
remote_user=wed
|
||||||
|
|
||||||
# (pathspec) Colon-separated paths in which Ansible will search for Roles.
|
# (pathspec) Colon-separated paths in which Ansible will search for Roles.
|
||||||
roles_path=/opt/git/homelab/ansible/playbooks/roles:/Users/rblundon/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
|
roles_path=./roles
|
||||||
|
|
||||||
# (string) Set the main callback used to display Ansible output. You can only have one at a time.
|
# (string) Set the main callback used to display Ansible output. You can only have one at a time.
|
||||||
# You can have many other callbacks, but just one can be in charge of stdout.
|
# You can have many other callbacks, but just one can be in charge of stdout.
|
||||||
|
|||||||
6
ansible/create_jarvis_user.yml
Normal file
6
ansible/create_jarvis_user.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Create jarvis user and deploy SSH key
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- jarvis_user
|
||||||
154
ansible/group_vars/all/semaphore.yml
Normal file
154
ansible/group_vars/all/semaphore.yml
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Semaphore configuration-as-code
|
||||||
|
# ============================================================================
|
||||||
|
# Drives a freshly-deployed Semaphore instance into its desired state via
|
||||||
|
# the Semaphore REST API. Idempotent: every object is checked first; only
|
||||||
|
# missing ones are created. Existing objects are left alone.
|
||||||
|
#
|
||||||
|
# Loaded from group_vars/all/semaphore.yml so that the configuration is
|
||||||
|
# version-controlled in the homelab repo and survives a wipe-and-redeploy
|
||||||
|
# of the Semaphore VM.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# API connection (defaults to the local Traefik-fronted service-name URL).
|
||||||
|
# Override semaphore_api_url to point at a specific instance if needed.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
semaphore_api_url: "https://semaphore.local.mk-labs.cloud/api"
|
||||||
|
semaphore_api_validate_certs: true
|
||||||
|
semaphore_api_token: "{{ vault_semaphore_api_token }}"
|
||||||
|
|
||||||
|
# Feature flag — keeps day1_deploy_semaphore.yml deploy-only by default.
|
||||||
|
# Set true to also run the configuration pass.
|
||||||
|
semaphore_configure: false
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Declarative configuration of the Semaphore instance.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Top-level shape:
|
||||||
|
#
|
||||||
|
# semaphore_config:
|
||||||
|
# project: single dict — the lab uses one project ("mk-labs")
|
||||||
|
# keys: list of credentials Semaphore stores
|
||||||
|
# repositories: git repos Semaphore can clone
|
||||||
|
# inventories: Ansible inventories from those repos
|
||||||
|
# environments: env-var bundles
|
||||||
|
# templates: task templates that tie everything together
|
||||||
|
#
|
||||||
|
# Each list element has a unique "name" used as the natural identity key.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
semaphore_config:
|
||||||
|
project:
|
||||||
|
name: mk-labs
|
||||||
|
alert: false
|
||||||
|
max_parallel_tasks: 0 # 0 = unlimited
|
||||||
|
|
||||||
|
keys:
|
||||||
|
# The ansible-vault password. login_password type with empty login
|
||||||
|
# — only the password field is consumed by Semaphore at runtime.
|
||||||
|
- name: ansible-vault-pass
|
||||||
|
type: login_password
|
||||||
|
login: ""
|
||||||
|
password: "{{ vault_ansible_vault_password }}"
|
||||||
|
|
||||||
|
# SSH key for the gitea deploy access (clone the homelab repo).
|
||||||
|
- name: gitea-deploy
|
||||||
|
type: ssh
|
||||||
|
ssh_login: git
|
||||||
|
ssh_private_key: "{{ vault_gitea_deploy_key }}"
|
||||||
|
|
||||||
|
# SSH key for the universal automation account 'wed' — pre-baked in
|
||||||
|
# every mk-labs VM template. This is the canonical user Semaphore
|
||||||
|
# uses to reach the fleet.
|
||||||
|
- name: wed-ssh
|
||||||
|
type: ssh
|
||||||
|
ssh_login: wed
|
||||||
|
ssh_private_key: "{{ vault_wed_ssh_private_key }}"
|
||||||
|
|
||||||
|
# SSH key Semaphore can use to reach the fleet as jarvis (admin
|
||||||
|
# account provisioned by linux-baseline). Retained for jobs that
|
||||||
|
# specifically need jarvis-level access; the default is wed-ssh.
|
||||||
|
- name: jarvis-ssh
|
||||||
|
type: ssh
|
||||||
|
ssh_login: jarvis
|
||||||
|
ssh_private_key: "{{ vault_jarvis_ssh_private_key }}"
|
||||||
|
|
||||||
|
repositories:
|
||||||
|
- name: homelab
|
||||||
|
git_url: "ssh://git@gitea.mk-labs.cloud:2221/rblundon/homelab.git"
|
||||||
|
git_branch: main
|
||||||
|
ssh_key: gitea-deploy
|
||||||
|
|
||||||
|
inventories:
|
||||||
|
- name: production
|
||||||
|
type: file
|
||||||
|
inventory_file: ansible/inventory.yml
|
||||||
|
repository: homelab
|
||||||
|
# wed is the universal automation account pre-baked in every VM
|
||||||
|
# template. Semaphore uses it for fleet-wide jobs.
|
||||||
|
ssh_key: wed-ssh
|
||||||
|
# become_key is Semaphore's sudo PASSWORD slot, not a second SSH
|
||||||
|
# key. wed has passwordless sudo on every host, so reference the
|
||||||
|
# built-in "None" key. (Semaphore rejects an SSH-type key here.)
|
||||||
|
become_key: None
|
||||||
|
|
||||||
|
environments:
|
||||||
|
- name: default
|
||||||
|
env:
|
||||||
|
ANSIBLE_HOST_KEY_CHECKING: "False"
|
||||||
|
ANSIBLE_FORCE_COLOR: "True"
|
||||||
|
# Semaphore runs ansible-playbook from the cloned REPO ROOT (not
|
||||||
|
# from the playbook's directory as I first assumed). Path is
|
||||||
|
# therefore relative to repo root, not playbook dir.
|
||||||
|
ANSIBLE_ROLES_PATH: "ansible/roles"
|
||||||
|
# Collections are installed by the semaphore role into a host-side
|
||||||
|
# directory bind-mounted into the container at this path.
|
||||||
|
ANSIBLE_COLLECTIONS_PATH: "/opt/ansible-collections"
|
||||||
|
|
||||||
|
templates:
|
||||||
|
- name: "day0_linux_baseline"
|
||||||
|
description: "Apply the mk-labs Linux baseline to one or more hosts."
|
||||||
|
app: ansible
|
||||||
|
playbook: ansible/playbooks/day0_linux_baseline.yml
|
||||||
|
inventory: production
|
||||||
|
repository: homelab
|
||||||
|
environment: default
|
||||||
|
vault_password: ansible-vault-pass
|
||||||
|
arguments: '["--diff"]'
|
||||||
|
survey_vars:
|
||||||
|
- name: target
|
||||||
|
title: "Target host or group"
|
||||||
|
description: "Inventory target (e.g. figment, semaphore_server, all)"
|
||||||
|
required: true
|
||||||
|
type: TextVar
|
||||||
|
default_value: "all"
|
||||||
|
|
||||||
|
- name: "day1_deploy_semaphore"
|
||||||
|
description: "Re-deploy Semaphore + PostgreSQL on figment."
|
||||||
|
app: ansible
|
||||||
|
playbook: ansible/playbooks/day1_deploy_semaphore.yml
|
||||||
|
inventory: production
|
||||||
|
repository: homelab
|
||||||
|
environment: default
|
||||||
|
vault_password: ansible-vault-pass
|
||||||
|
arguments: '["--diff"]'
|
||||||
|
|
||||||
|
- name: "day0_linux_baseline_check"
|
||||||
|
description: "Dry-run the baseline — shows diffs, applies nothing."
|
||||||
|
app: ansible
|
||||||
|
playbook: ansible/playbooks/day0_linux_baseline.yml
|
||||||
|
inventory: production
|
||||||
|
repository: homelab
|
||||||
|
environment: default
|
||||||
|
vault_password: ansible-vault-pass
|
||||||
|
arguments: '["--check","--diff"]'
|
||||||
|
survey_vars:
|
||||||
|
- name: target
|
||||||
|
title: "Target host or group"
|
||||||
|
description: "Inventory target (e.g. figment, semaphore_server, all)"
|
||||||
|
required: true
|
||||||
|
type: TextVar
|
||||||
|
default_value: "all"
|
||||||
@@ -19,6 +19,15 @@ terraform_server: "infra01"
|
|||||||
# Traefik variables
|
# Traefik variables
|
||||||
traefik_server: "lightning-lane"
|
traefik_server: "lightning-lane"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# JARVIS automation account
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public key for the 'jarvis' user provisioned by the linux-baseline role on
|
||||||
|
# every host. Public keys are not secret; the matching private key lives on
|
||||||
|
# the JARVIS command centre (carousel-of-progress) and, when needed, in
|
||||||
|
# group_vars/all/vault as vault_jarvis_ssh_private_key.
|
||||||
|
jarvis_ssh_public_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID5sym5ajFvDyzw395BkHv7qVb66XPTx/OF1p19MGuNo jarvis@mk-labs"
|
||||||
|
|
||||||
step_ca_principal_mappings:
|
step_ca_principal_mappings:
|
||||||
- local_user: wed
|
- local_user: wed
|
||||||
principals:
|
principals:
|
||||||
|
|||||||
@@ -1,306 +1,436 @@
|
|||||||
$ANSIBLE_VAULT;1.1;AES256
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
33333438373661633337383161343862383963623736636136653339363064386538386437323135
|
65653162653536373535643935613236366335356333336264346637323164633739633965656664
|
||||||
6436666330363032616365656434356161396530363363610a333864313064633232366133366232
|
3434656433323239366665356134386564643135663135360a653232636463656332343565313763
|
||||||
37316636366334346536663337663065303635626638666264666435393933343832653061323237
|
61326635636333393730356131356466363063613734653565643766383431363166656532393934
|
||||||
6663333736386636350a653931626665346234323832393334373563303130626262646565653231
|
3631373264353563650a616362633165666239323563623161656238633737316236303133313034
|
||||||
30326263623965356639386438636531306430343239313162383065386135653534636134376333
|
66353461643535373638616364663365643961656635643566613336633466343739386234313532
|
||||||
64616432303732646535643530353963363734663538393539633034326535643539356561656632
|
38353066313763643266623739366261646633363962336239623830613265383335383763376534
|
||||||
36363330346131393262326561356631343631356434666264616134643136646238376336373665
|
62653865626662383330656530336534366136313965373764353133326139623132646364396337
|
||||||
65346361636435316437353235393263313766623438336662353532306463353134366435326364
|
33333366366434393037373361376538376334336265663162336239646237306332623039343032
|
||||||
36613865343264616364333631353238663238313338333232626262313637633662633163366638
|
37333832383762663534643638666234623062373734626335323537616334303638376164656634
|
||||||
61323166393739656536656435336538303439636561363961353832313431666432393463636537
|
64623962313064363166383930636136383935613237613434373733383836666532363265643739
|
||||||
62323939376265353330356334303334303532643136376531343765613738656562386665336132
|
37376333363736326334383864633734393263623536393239393564643131616132373230356633
|
||||||
36346232313432396431666535663362333332613037623531636135376439393936353261653863
|
61636165333537356632303034346431353139383065393832323236626331353236666263376666
|
||||||
65373061316461303866363338376131343736323933383636626338616431656533363833663266
|
38306631653862343064656135396638636363613139373062653735366565376165353330636162
|
||||||
62363935313262393733333566386337633630666332353263666462373164346362313034636239
|
38653530366263356437323433653437373362356166633235386330623333326264333961643434
|
||||||
33656462396238343065613965613232623132343562323364653366313231323531326364376631
|
30323130613431353064393138303465316630383462653062326264393035393735396134396665
|
||||||
65643961303862653030343364326533356336393031636437393465636263626236303633326139
|
36383963633938363662633035626538663366323461656136323934353731626264396364643337
|
||||||
65643432663037333332346261323663613031386563383533336538383133373332343363326530
|
63356337343161366237353135313966616630316362396362343962356564643365626664633165
|
||||||
66643231666636366435623639643166333863376533633537363364396663363732333932663333
|
62383765303436303239376365316632353463666664306164313933346162343833303963313835
|
||||||
62333239613264353233303064333931326236396538323130353061366139623362393734323434
|
62353565333231646462383664356230623165363333653335343438346233316534333132366634
|
||||||
36343537393363343861633131346561353637646336313233376162656561623362653565623339
|
31326138663433663132393761343335656636366237656337666331633062303363633938303536
|
||||||
63386337613461343534643964323932356661383436303966346339386131336133643132376330
|
31303238323932336330363064323963306132633961313634616232636163353439363666623230
|
||||||
38656535353335303966636235643666306336396435636434363733313665366234393032333065
|
65666632653730303134343032616331363932616532313365633038623135396463316666373637
|
||||||
37633231376134636435343233363032666231666134623365653238373836326237373831326262
|
34343736383731363239303330306561356162623962396564323239376661363761336434376264
|
||||||
66303234303164303961316166623062616139353264343864363938313733653563663661633535
|
34373037623331643666366165306161356365326261366566323233656538363061636637343164
|
||||||
37326636346435626362336437666364623264363565653935336438386262376663303230313265
|
39323130613530386661343839623361303862323763373864626536376562643766643263376665
|
||||||
62663665666132313436663330616230383235346333623563313262376530356535613263316436
|
64373334333436653061653962373437366437643034306633343830626230346532653565353135
|
||||||
35313766363763386234333733636464326134633136346532346632623534646365623136343363
|
33326339656266616237643362333062346564333563326463393535356465666166626463643132
|
||||||
30316435613031333036383835656230353430636135343961356363613139353763343233633834
|
36316263323761653333363632386261633432383431386435363362663734626634353761663035
|
||||||
38656431363139396535653565633631393462316334666464333839343035616531643466643531
|
32383464396632396361623531303937383366633131613861646364303662343761323530303139
|
||||||
35613733373331643961353830376266376433363938373563653833343464633465663766333066
|
39333866663164363431656233663064383633386261313063646435333666376465626532343636
|
||||||
34633364313762633034633461623232656537653762623532613332386132333365383062653136
|
62386166306635353061623365313738306237616430386461343262613039346261353662346462
|
||||||
63323234626266336462333534636236653230343530336532653831313339613531343731313237
|
65616261656534336631646434316336646337333661336530633834633037363834346539646466
|
||||||
39653737663665373361326433643364656434393838326339636238303964366336363961653366
|
33346466643238343133326339393264613763626532633938613765373262393236643533383661
|
||||||
36643938633635313432663765316130326238306163346566313062626439626263633033623731
|
33306233623130666161326365366164333434373463323662316432633263323761316338623065
|
||||||
63323532346366653238663931653334613734633963363632643462316130393138363436666532
|
66306332656438303938393430633563303761303239346266386664333837613336316539643736
|
||||||
32373833323330356164626332646232623031326365653765336261323661616137373732356164
|
31353234613664313061306433656165373737336166343930653837303532346163613564376661
|
||||||
63383633353764643233323430333233306337623834353934643864636339646239636430316166
|
61376237383562353639393565303332633366376632386532333736303738333362353334353366
|
||||||
33643030333261323461643031613730343131343830353463313766353035326436306566343161
|
36343166633533303834346265666536353633343564623062613030323934653863343062323136
|
||||||
35643534323763316165343832346433356364343061383036306434386631356537363331303035
|
34323635363238623336653062666636646263343731316537346635346235613862623530326633
|
||||||
34303430393034393763343964613935383739656662643066363633333839396465386231383837
|
30396338386233663335613661646132386230356631326331653962393336653937376264313931
|
||||||
30336331393365346636363732616661633635326364366435366466383639353839343731616365
|
65363465343265643034363963656262666131393632653561626330396166386566396363663538
|
||||||
65326633636263653438343561633632646234316461616666363630333966326230356331373335
|
66323935383166623038326437323730363266316530313938373261366363613731656638386634
|
||||||
31353665626233663433633563623765343431376639316232346666663065656462616438663333
|
31316261616630306131633936326635333632643731313730303438656563343133373732376537
|
||||||
62663662336662346563333935333261626635343764356631356363356362346231306535376466
|
38623330633539653034343364656261316336353466653663646333653636653266373363333732
|
||||||
36393932666662653934626134613065623461303834393231616332363461306534303666353632
|
61333461346131366233336162363033623636386634613731303032393738333537393131316539
|
||||||
35646363333065356631393863396536633838343365343238636332303862306631346639656166
|
65643238613432306436376432373364656530323331633762393566396531396466653463353832
|
||||||
30373735336361653863326331373834663164393863666631623866353338366561326635323132
|
34393662616435343137373364663839613464346564663631343837366530313061326561656664
|
||||||
35306535626564376666623837666435663337613164623966306138353161313239373436346239
|
33623163333864346661393634333830336133383337373833346132353531656236363661323338
|
||||||
36656266323233636233326139326266346464353665323465663666646264613466636132626464
|
64313663356432373137343461333539386665643130316463336134396436623730666165623939
|
||||||
37353762306538643233663338613062353134323832653139663036666337643131613062643936
|
33373832353932386238353866336130356530613766626235316332623132343735636335633437
|
||||||
37633363376365356333353433613839653130323036643737633163336531323032393937333432
|
37303766386434373930383663386565383732636635646133393637383963353134633433616431
|
||||||
39343238396534353330346664346135326333333638666261396463656663323935643337313462
|
33616464633431666361393931623631313731343863636234646130313136346639323662373236
|
||||||
32396137613432623664323134356361323861306230353165663162663732326563626162346630
|
62623830633838326432326339383533373430346635643739646639633739343639636665376333
|
||||||
34626536393962393764393764393234373138656432346332633963393135386238313563353761
|
30396238356161373734633562643466643036363239396538613465346238356366363730623965
|
||||||
34366438353337396264373032336166663937343931633635666166343638386237396333626133
|
32336533303331363336666238363863313066376230353932323466316432653264346334373931
|
||||||
38383766616566336163363931393438366566386565663938356630366430393830653037363738
|
61303066313831376233313261633336643265396139663037333031636666383738313763313366
|
||||||
30666465326266363834653934643434666466326632366463303965303366326364316430366561
|
38386138363738353265613363353339323930343934393036343234626162353037326638636236
|
||||||
32636631663232373163636535366333623261366466623262393631626334626263656534386536
|
66343634323136663134373737366433616632653737316534633232323237343566623361666131
|
||||||
66323833353264623538663232376138346361646362313565333534363535663031643833373064
|
34393331373835613934316235336635666633646465373866356566656261373066653937316339
|
||||||
38313062346339636335633962323933383230623634303431383236626166636535326465316439
|
37396366643564333130383237613933613736376161616231666139643030613338333133356336
|
||||||
38646636336130373763646635386265373065326263616438646565313439613830316135616435
|
37343362363433653638373335363130313362306236393231646637383736663236376537303533
|
||||||
62326430336337643265306261343036653938633634626561333664313035393933646533326661
|
31633230333933666131613764336532373633353132373839326562356438386236643537386231
|
||||||
62626437306432353938356338616462663130646530623636393233373837336138383963323165
|
35616662643431383661343937613738393963373865383465306532336137373730653832633930
|
||||||
35643231613565313434386163643637633130626435376561376235346438303234386637383835
|
37656661326434633730346364326661653937313561333437383064313933363231393164616464
|
||||||
37356238616263646665336435306233396436666664396532373063626162346237376662393639
|
31333664386134376264376339303838656431633966663263303465376161633866376161313531
|
||||||
35373665373638396434353062666430633362653163633639623836346438323632643164653638
|
62346266316533326639303661396164376666383864363262316630636561326366373363633063
|
||||||
62623463343465303236346562386338646536656438633966643430343266313935323630616633
|
33666263333132323232346235633837363138663539366131303633313662326664373332353664
|
||||||
62313531343764343966333439343866613965303036616634363162303434643533366465313562
|
39306235313732376164306164386534346666633037316334366431363063663137356464656234
|
||||||
62393666313838313938356265633531326431333730626632343139666338656465306431636162
|
35656164303933306661373932663831346531636530386139373837633263396632643235383361
|
||||||
66343532346238393464366134656334363964626264623263353537633638346239396539653538
|
65393939303338656537353130316361366139363933363031393735666336323931373632303663
|
||||||
37303339613036306533353132326534306433353364613536316263326363323134333639346236
|
63333137663265353962653861316365643239396562333933383964663730663230353331386638
|
||||||
39376263313431666462383863336638306631663563313364333235333338323364613361633532
|
62396435323162663036303334323936633436626136663537376562363430376239356136343261
|
||||||
38313263353539633662353533646333353262356436353938303036663130306362303935356136
|
36323062363935643562326161643065333437343331613135383932333835396565646631646437
|
||||||
63313234363138383864613664666635613464653765636461383738356135623662313866383036
|
39633035326662653334616366323736323032616264623166326563326361363263623736623739
|
||||||
64626437343465333563346434323762313232643230323336366539326631396463303961666461
|
62633762346135626666636463343132303433346330303631656634646537616537363764626564
|
||||||
61656338643532616337653362636362373837373836666535633762343335623033316264366261
|
30316539303964626635613338623261613430656432376265386636613566313732386561653564
|
||||||
62633137356132306430623537616135613965393737323263343463633839393165646630383331
|
66363538643463653233373433353965623765303038333931623036666435303330633136663966
|
||||||
36616339346534346636396532333836633436323232323364303963313030656330386336633538
|
31313964323439376637613530343635376166333765306466353337623633343539313330373437
|
||||||
34383534313236396461653462306130376462396138303561636331663062303864326238303335
|
64343930636465636265383261633664313762303533326234626461623563636466643039343336
|
||||||
65663832333631613963326639386665643135366632393438666361376334636233336239633837
|
62346462343639323862353036326335383735323337396465613737336661633465363330363161
|
||||||
36383761616364363262306535653065303536363066316431303464373230326436336661396231
|
38323561613935303334303261323032616332333231363762303831373961323033626663383637
|
||||||
66333438376664333731333630383031366237396638326534636532343535303964653436633438
|
35323134666636353766313330316231323938326532333030383638646230643735653663346634
|
||||||
64616263366561653334346138393866353437393037343930623237326231303261383733336232
|
38363261626663393139313031633032343038396433353563363531306232386539303734323463
|
||||||
37646533393439653965386563333232656565663531396663303334313839323361363864396332
|
63313938373735383631373665613333303530383335363262306230326665666535333564346163
|
||||||
61656638303130303433666561646663323139643337326561333832373538306236326538363637
|
39336132643464666538356461393330616264383632623037623634336530376164336336323336
|
||||||
38666534633330653564653066643335623938373331376339393338343533366436333330313165
|
61646165333835333961323439663864386562326131653564623861386336303934346263643036
|
||||||
65366562386366666632653064663637343536313462326562626436356363306134306237613265
|
32666632623537376537646634313839363038666336386238343531323664396461633030373334
|
||||||
32366338643237333739626331393763323934396265383138633035646533353536633866313432
|
35396463373339633931383636353062396562383763373939633336346463623733303039663733
|
||||||
38336137383036373434363530353833653938663035306337643638316463373762613931373038
|
31663834326661623064323132353431366362636466623332363532316435386333636562633562
|
||||||
32373361326136616331663733616133623936663530356566383461336334663937383230386462
|
36613465363936623462316136643664336261353938653362393938666266663330323163653338
|
||||||
38656332613763653339363565613762383735333163633831356364333563363630623666616430
|
63333732616330323438633038366465353865353965663333376466613834633166303761633361
|
||||||
30353237653730613361643233386139303639666137653733353334326530306431376666663664
|
37613863313230653235653034613137303231343961616330316664653333303436356561353562
|
||||||
34633531393261343165663330373961646665396139323762313663376639366464306533623333
|
37623839646536363036613964336230373537653130366330633137356661366561386566323165
|
||||||
62326432623334336166656565656230313063656163636561326632666438633966323930316264
|
38653633373538633762643935346661363961663265616433383832666635666331366635636637
|
||||||
66373866663461616336336264356561373232313737353836646632383438333162383832616662
|
32336532373164373135363533333539623739383330646431356363636430326433376364393265
|
||||||
30636437613163613631623165366631306438643765383263626133653231333361383633363136
|
35643561613033623231366362393737663964396266313232666362326436303331653764366436
|
||||||
36653831623562653838633133646633393038666164346461333531373034356435363336636338
|
30363135663234356363613065643763353836626133656334653138393562393032623631343130
|
||||||
62623464376264363537306166613461396630303039373239656664396564333435303164613235
|
30303936656636366465633163323061333863393532366563316233353466613566316563336539
|
||||||
61346238386233653462616632303337663036626465663361636130333566303337313237626237
|
33326137383263393266393166336361656538353963313366353434333864623238333435653836
|
||||||
65636633316366343064663636383362316665633263633136323563363935346138356635653036
|
39376138356461663262356665363835653638623031396539353132623737333736353937623533
|
||||||
36383835323838366363363335323731333066393334373265653563336533616362633163343661
|
65386333396666633365313262323739633461383464386663313163646632646335646433663834
|
||||||
66326630353537653633316631343332303231373630363635623136386537666434383032323165
|
34616663386661363135653765386534633339653232326133396332646136653230393431626637
|
||||||
33333633643064376239346139633664633330323966366430353135616633633465363030386665
|
30366433336633643837363434383439343562366164353235653833306138643431316537343931
|
||||||
36353164623564343934323063313763316566373830643163616633643937323732333561353832
|
34396535393432383163653231623837633961366437653434623164333333323563666330613866
|
||||||
66623364666335316230346665353631663465633034323265666663653866623037353935393262
|
36393561656135303731666638393737653565303836326264373137303264656334653163366433
|
||||||
61623065656637636666383239343661616233396663333038393963366537326136653863333235
|
35376362326133666336303732656561326164663832623732613830393063343231323363353366
|
||||||
65633137626134383763663366396438366634313465363965343830333966373930333134353339
|
66636233366436363565336231663964303532343962333732313832323762343638356231343461
|
||||||
35396538303537316636333339613731623636646230323662613663306537326566656666323434
|
65373062383564346266323362336666613735316161366136303032313039373464383532343932
|
||||||
65346531623533336535393530373963343864313963666335636334396663393837396665373565
|
30396530373030303933353264363330353132376164303062623538613632323932373230363838
|
||||||
39646138356134313732666633663061646330643438393736653433313238373135656636643337
|
32633766316533316665393033326161373361333166643436356430623262663032646262616633
|
||||||
39396362383363333434376336613933613438653636663334336337653633613433623336623461
|
32643436646264343366343862356339626561346561353463616534306139383133656433636331
|
||||||
32373565306435646438306365343465653863656434663937653938393839333739363639326536
|
32333735346163353434616530623235373037376664326266343933326439663965653865333766
|
||||||
64303534383961333333386237366239386330386533343939613664363439373135333731393739
|
31653163643134346331313331633561643336663130353936323439636331383134646665656630
|
||||||
39363166306636333766333332393265613133356262356666343039666237663664636266626664
|
63663866663938663632646664323936613434333731306264306633643335393566343564346537
|
||||||
65316339323638353333653631363365356562623535666235313234613236326130616261663830
|
35326132303632373731373532623662373861356534343536613731366666373439343563663737
|
||||||
34663063626539363734313562656233656131326265626637353239636638396564313561383761
|
64326339313533323936393362613564333065623731653930363264373061636665313135623062
|
||||||
62663638623135343333316464346236353364616239633566393039633339306334623234616331
|
63373565306535373763356664333465343666626439626666393734303962303762393066643139
|
||||||
34366435663938346335323932633330366639653636336636373562306561656536653363366233
|
62663334313736663132386232613061326636313664326462353361633362323464653038396662
|
||||||
39383064623435356338636335343662343435336564363965646439393964323162363736346637
|
64363862356439633564313065363361313161643962316166643936346539626339396230333633
|
||||||
65323765333364353831396433666561376437333834313334316165386631616332633533666336
|
39333132393135613264653363323233643032376266313630313735373966373966386239336635
|
||||||
37313533623535313934393230353439363231623063663961376537393638636237623134323337
|
34643739363935353463373434343430633438346136306337393061646139346230636531376537
|
||||||
30393032333530316330333736616463633262616631353238323933303766646137386465643366
|
36633861303264633662373365623261303635363836333163623864323338613739383134636237
|
||||||
33356665396437623732373437373537303034353431353031313162323730316566396238653038
|
62326561356535626564633038643230646431613866643239646563323035353434313837663863
|
||||||
30366234663037653736353137376264363934646366353761323364663538346662323934343266
|
66326530353265626361643738333266346339326532663166656432613631313137396463643864
|
||||||
30373662613461663561623466633939396439653663353365346133623933613462623231616562
|
64343339373131363066303437633436306462316166356131366134626331343635323837643136
|
||||||
66643738643661643766323266626266313330633936313964366536323263373666303130623738
|
39306461316230663134346563313662616532616239333735303061373138383363386232373062
|
||||||
32323230356665323439646532343930333936373536626466656566363161646663616266326133
|
61643838366266613637336433323062336434313836376434636433306135303734376661376361
|
||||||
32393137663562613531323832313565346632303131646432363961613331346636653830323136
|
38373438623331653165643837303733393436626166623762623531316164666163316661643761
|
||||||
36323436373964313737313131346131353630636561636639316464323666636336656265343565
|
39616539653831336261333335643834663533363233333732643431363836386633663539386638
|
||||||
65303936353165326137613638366532303265356537343337656165366663383961643134656435
|
39356336653438663061306230316464616533363036363434663335383530326432376632396338
|
||||||
61383565306265333939613561613035616663336361396536343939343761663833346134646365
|
38386130386532333861353362313838383964663861663962353039613565636339346162383331
|
||||||
34323233666439623436323261616432363838616437386138393735383364393164646638373630
|
32313337353430343130326466623666326263343438663463376134336131323735663261356263
|
||||||
33386632346239383037633132636431366264383633333365306338333661303662363533376339
|
34613638303262366666643036353766653039393739303738366330643033316632616232393032
|
||||||
62633663366362303038616666336335346332316366353136323666346531393938666236343163
|
36336161646434373332646663313834623064336437316666623663613861333838363731336564
|
||||||
31663665643839313439663839353431343061386465366331663637373532613266383065393639
|
63636633623664353432323836306564616239343731316335333234336465363232326430643231
|
||||||
62613565626135363133623261333061373738633333623337396631356332643039306638303434
|
30623063316130396531376237363363386465393838303463336439616333353338366436323634
|
||||||
65663762373630323263333239613939346133626534613632396433376234663636363062656537
|
66626463326136343533343263303033333766306632303333376136643137353831323135666430
|
||||||
62386664343462346334663662663865653766373738306438316533643962356136653565373761
|
61313663336363356531633866663366333433343433643664663665643335633035303034666334
|
||||||
62393437326138323065396630653838326138653865643937353431656137393732346361353734
|
62653233616136656462343930636534636432353237363538356365636635373865353532643764
|
||||||
31326634653331353232633061303230393462363065613832303630383237366463363666613864
|
32646431376165616235396133323162373330386664636336336266363961316462613263303131
|
||||||
37643135303034326335326563353064343835323863313134616262386564313235653762376332
|
33303635306136303366353331653239306130616464303835363437313566363436663032353061
|
||||||
36636533646235343138396537623266333566633466393764353731393935306432313266636133
|
32356639333061636536383238396665393232663562363662366563653831343037626535666432
|
||||||
63326362653033613264333139633638643638326532313239646239386332323330306538366362
|
33353261623936313563323564643134666232333839386162663331326461393239623135383530
|
||||||
37646432363535396161336465376363343430333261396536346464356135343462663462613136
|
31323861666631663933636338323239386336356339623738646430333136336635643562353263
|
||||||
36666238633663326230373834303431336566366666666534343537313635366537396262656236
|
64313931656435383134353063383665646538306661396163653434373031326134336235646461
|
||||||
36646639643730393134613662383238623835626230313833653932303832323366343765343434
|
39343734313965313365643135643166366133313465323366633038663333366535633532623966
|
||||||
34363033323836663035363166313630313062663162313165636138373261386636623831666632
|
36666134316165613830323933333339336663363964323764313430613130653236336664393863
|
||||||
33373962626237666130373031633437653966386561363232363332333633303961323864333263
|
64306437316235306565333238666164353738336539353064343161343834656262653666636663
|
||||||
39666230383332376537313137643635623336316336646537333137633564376432356432326639
|
36353063326262653466303534616530616139636166343137666439336335363539393439633436
|
||||||
61383565313233376435636366366531363462306535636131636163636130663732383161616364
|
36623963623839623834656431396162396562373330316438383739363637613366613163336138
|
||||||
37343064333264663533343739356139353365386565383833363165326536306431393764303632
|
35373730383165316330366332373939643839336634613934653538303866343738656534653263
|
||||||
30653865623566363965306636366530303261616663306433393135656366383138356337383365
|
33303361343237356430373266653062313037663230366531366363393937653439363732333036
|
||||||
36323736643035383231663034626564643235646237623632383164633062326132363331353634
|
31343365633862623038316536363031356336383461396562626236313038313239623665316439
|
||||||
61653761346439326665366565383534393635616365353361353863363763383664303166383434
|
30336137356438343362363536303234346637373833313231663333616263303233336161383130
|
||||||
33393732313837373336616564343864633330663566653933356237613538653366663838383132
|
34316265613736383830363639366139313537313661633736303634326237643330663735356561
|
||||||
33303437323438313837616134616466626562313262653866313034383764656566373331343738
|
65626136613831376432383363383731663763356432663264346561646535633966643462326232
|
||||||
30613062303630663365636230396231313065356662376163353632613564363066316239636434
|
31623930303262616535356633663938323635643462663731366433623961623435303062616263
|
||||||
66356535386263353737383838653538316366343935323337386561353533353161636538646363
|
63353836333235336362353039343431313262646137303364613838306339623930666466636132
|
||||||
63643739376366326536333261306263653563353331393133323964653764373932663136316335
|
32656434623738333565636164303535393734346462353436313030396264623332393932653861
|
||||||
36333535626231373864316634613366353535393932373039646161646466363663626561373033
|
36383533363030356664326332376662616139343065343932656262623334633334633462383635
|
||||||
64333133353931303361363466346666376431363336323166356162363537633536336638653838
|
63666439343130623936376636633635613335363264363136643366636535613938373866356336
|
||||||
35323562653138393833383130353232616564393738326638366565616233633634336438303632
|
35666632313565633630373039316262316339333035366332356138393261613836633437363136
|
||||||
30353765623565336330386335306162336662643466663335623836383238316161633339373137
|
33653033343536623264366466356561323063313031633464353963383661316131643166663566
|
||||||
32393165323735643034633464313337386362363331353432363233396538393835383933313637
|
36373937326333393164353234336433656336626134363236303032396531353862333535336466
|
||||||
65656330383732633537646361666439303266653165313233636464313964383639346635376538
|
65393865333565386538386665373166383235633739383934643334653763366161396565616166
|
||||||
64633861623439626331643431326336646135656539313836323135373862316438393237663564
|
61333666353666393230326134636661376237353937646236343034626135366535383631333035
|
||||||
31373135356135356662336265633862343435356530393835376430666566653036373338636332
|
61346231386233326564656636363066396636393966353539666231646464323636303038656562
|
||||||
64663230323361356262356537656262396665663538386664363735646234306231613339636338
|
31656238373331303031393034346630643263613365643232663861373336363662356265326164
|
||||||
34366433326535353965376236643661646339366566663531653864316533356466653864373633
|
61323866663033636534313365376466303538666463663539623231313632303262326638633539
|
||||||
33363330333765373662646634326236313434383433653563383436316635353461386662646165
|
62313239356365313932653735363662666266643765633138393037653662656566656130663133
|
||||||
31626566616163373137326165323066646535663766636265386435633965326165303831323633
|
61373531316261646234666361313766626139613762306563343561363961656565666163313532
|
||||||
39613139623835316165326138386431383530383035653931616334353235636138303761383037
|
35323837333737366138316164376537613834313135306461633735613362376233306134373537
|
||||||
39336163373332346365333935656563343939623335313461363138656239666263366236663464
|
65323339393039336230373237386434333639623762643133363339303265623065326438623531
|
||||||
32303732663937363964346639323236623166373334386239613564643834316536363634326461
|
65356632363237613937633132326231333938336639663438653861353761396434643136643238
|
||||||
64623939363635613863613065656363303734643766306564326437663037623839303734393036
|
62363162666366653831626531353236623532656437313134343633626666383437363761626331
|
||||||
64326363626562363934313930383766383462663766336666663338653765656137393362643366
|
35386431646564653233613632373435623665613335316164636562356264663033366639636439
|
||||||
36326336323638393161353464396637633566383262333234656233653334623131323532376637
|
63356231393362663736366431616161633065303537366538393935363033303636653836346132
|
||||||
62613064626532616631646232386631623839326665346431333232333463373031316237363462
|
30633036653836656433636631313863303132666531326563303266316566623934666361653932
|
||||||
30643731653666643331643839343865646437613135346363646131383262396134376363303335
|
35623265613635343435373361353635343261656233356535303037383433353731313539613463
|
||||||
34303164303236383139326264623462363062313139653465346435336163653863633764613631
|
64363334383666663639623735393934646366386437633638613034316366336337316561333731
|
||||||
33623666316530353163396539323238663935643764643939333966336137353034663164643733
|
38363339386561373834333037626565623335613563323366656365643032373834616339663838
|
||||||
34393132323339626331626532643266336435653636353930393738363134353661653636643263
|
64646534623432323363303032323666643636323536666239616238323932353165663535633465
|
||||||
30313138393263383837333438356463636162383938396137363035646433663932613066386138
|
64633934303565396533313165393438373231376664616237373562306130363531393833353365
|
||||||
37316535373939326131643930353435383232623366393630663463633233393232373730333631
|
63623033343762326437306536663734386365653131346235303837336236366665346234316163
|
||||||
37623837373533383462323431323335316231356436303239313535633837353931613730303363
|
32616539666634653839633739303837646563313364333864343231306663383530313963386434
|
||||||
31613765626466636636613366346166393766373834643337353764666532376335376139316138
|
34313965393731653963643336336134343764343065646436303739356530333761386465333333
|
||||||
38646133643736636363363434306334373933633264366530373534666637393835366266306633
|
35653363653361366462636162663134333431323866306235343764333035373534656138643933
|
||||||
66653966613136653936313732396366323830376236626530306139333030323966383035643733
|
30653962613339313833343532613939636332633236303733313135313932356136383439326635
|
||||||
36353833323539393136303035393866366563323430636336356639623734656135356566373062
|
31386565343337333662663762356361623835353830333865613437336532623936343365343231
|
||||||
34666433316139316664643234623530363732373338623162363636303463346463323131333065
|
33336137616266303835366662643737666333346433366663666462396531643463373562623237
|
||||||
64656566353837336266386233323439623237366364343635306137663038326434366531623062
|
38383537666639363839653538303266303965613762373561643433376664313966326561653764
|
||||||
64613365323339613465326464653965613439653766623762363139366264663765363862323839
|
66396566366265343962393633653765643563666634646637623161323538623961393039333036
|
||||||
63346539323961343532626465363266626234363964313531353662636635623165376130616536
|
33363530653935386331396533666432626634616463663763396663353133393461623866383061
|
||||||
64363361383766636333626366616531343833633033643965626435313130653938316430313532
|
64366132646132663263636131333031643736663031663336623065386266653638376639336334
|
||||||
64363731633139383236326231653434663132313463326563393739393132663765396337643035
|
36653932643831303931613532333036323766376235303433633564303062623336646166303533
|
||||||
32373963393830356362366130376135353935396631393533333430363531636234333832373939
|
63353063303033623032643333653733393332303466366538353062373666363438366432316131
|
||||||
36656233343663623262346337323034653036373432303266353333626434353137353939346665
|
36613335396531663238343139313061613363393463363035366132353439353433646237646265
|
||||||
37633432303162323538343339643635636464353333313063363662663033396630303162333639
|
37633964316466636337363331383565323866653036386435376530343661323565666539663230
|
||||||
31633537373364666234653938633461343738333464653736316331343038393333643936646462
|
38386232376436623064613232636637313931316535643661306536343661653135313335616534
|
||||||
36373232373962653031333730363166393463313232616631383233386533373464366465623335
|
34633761663066316430313361393063623033373636633133653536383764306433346235343761
|
||||||
65393462353638633931353331313233356230303565313030663739626230323766666466326339
|
37393764633561653632653565383937383436666662336633633261653861323533303732613738
|
||||||
35303361383065353333666161306433383139366439633730666364383130316637633464646336
|
64653739323636613639643465316437356163356238656139663034313738363532373861393333
|
||||||
66643065303934663765623233333261313939373236396136366566306438626439393163636430
|
64613038386535396565656538663163313161353831306237313064666566336534646233393137
|
||||||
30656334663361396230303239663338373664346362623934313130343064353435626439356562
|
37336265316361613430663961356361653636636235323439306537666363353361643664363631
|
||||||
35653638613037656133313765313165333066353732363361386234353635626563323932356237
|
63643631336134323032636463393639613636643130313136326538376461363937643436633561
|
||||||
31363333326536353536323931643739333764323637356164663566343563633761333462383735
|
39663433623663346462356437666230313937396132633834386661613436653839616136343234
|
||||||
66383134646139373130386366316532656139336231303931656265636130643130616531366530
|
35333437386663353863306166393634363734323636333137343938353461386366326239396236
|
||||||
62383132643236623835323362383866613765343762333536333365336338373264633130656438
|
65303939623437376535633463626166653935343033303066383064306332356230623566393062
|
||||||
36343331666431326530626137333665623836636136303031333965356366396239396534663263
|
34666566353238653730613561383434343332333732356334623836616334393464373965633334
|
||||||
35343730303534313830623430666566386564653036356635623038303133623231616635396463
|
37313564393463663862386535626338336235613132376530666635343832386231366533363562
|
||||||
65626137633033663631373366373238353766323533643462396562633164633730633135396266
|
32626162626434366261623431373938646237643462623337653466653538653732393634366639
|
||||||
36316362353964306463336237383232306530613539653462326137383938333638353038396132
|
62666231376239393066366336386166646631396632613631313663633435646337313465643231
|
||||||
66343265643162363735663235353161393732326530373735363662633165316265323065396163
|
63356566333764333238326165613832643161346366396635663032623133386132666333343337
|
||||||
31663761303637616566396262346238366631663562323566323364663464373930373831323630
|
63323637383035363833653162326231616461653062613765363165313638313234343266333334
|
||||||
39333165336264336539353634633936396561626363353466303931343436306636356663346438
|
62383362343232363431613364383930376338303839633261656236636264353335333938333731
|
||||||
64373561666638656439353964626436613164643663336336333337393332346132313830316430
|
63616666626437643432393438376661616262643939363239353836323036303139666338336261
|
||||||
37336139333539316639646337383737373530643533336133633135333936343165303764623366
|
31306437353536623561333531346630643761336235373230373135326361393739613934653361
|
||||||
35346432656263396439663635653066303861643938633033393334336336396133626237336432
|
38346532313364633638306665306563646433353038346238313530363163396166353535343537
|
||||||
31393061653839653964613239353336653666366163616163616230396661313233613934326339
|
34343330373063383566333335386162326339383835373665363464616139383331646436343365
|
||||||
36333733663032613633613037343337646237666565316239303231313163383264376636336338
|
30386564323733393461653662323334656637363566343737356239306462303762383332656239
|
||||||
37656532373634383132346163316337336534316465383531316465353861633062336235376631
|
30643439343161636434656338636533396364643632383030646639356534343963666466613566
|
||||||
33333963313730653164313866323461353334653231333762336232366434333631396135333466
|
39373833633265623563396363653830393661633636646265643362633731626462636531313362
|
||||||
36643362653137323934323966303734316334306133656139613763336431323236643264663530
|
35366331383635643564316461633431646565393863373635323834363934336335653831613330
|
||||||
38633730346531396435666530316563386566323737343766653135386537393165356264356534
|
39646262633636343832626362393730643163616565363765346266316636343065393630333866
|
||||||
62323962306332393063313062313230333334353833393362656166353638323366353934656263
|
62333931336161636230326132393739616665646462653666346532356265643235333131343635
|
||||||
38633531306438663134616262323332323338653361356336353962376331323537623734316532
|
31326638626162356333356261623531646464633139666539373261376230336661313937363036
|
||||||
35306434383336633436656437663765626530626535383464356532613536356533646332646135
|
32313464316364623838343231346538303237393137363233366234373031346238303932383461
|
||||||
39613030323432643161366363313262633261366566613931303663636262353466313933303162
|
66613537346136313262313337636565383639383863346535393361363930616535626264376565
|
||||||
66373535353934626135326439653630303437666535396266303532626636613563616633623237
|
30303236663065363161663836356130363130363339643335653366623065373566353136353933
|
||||||
38623938323137393065616136376663363234633735363264356438353534616338393832386337
|
39363761636130646537393166623335353431616462343364383535393661313738343265313138
|
||||||
66663339313062613766666435626364353935643932633233643630323464336135336339376437
|
38323066333239616137623233323664616232323262666530373064366138316431643430643462
|
||||||
30323536373639343738613463613466396335626339343135306666663931656564393664643761
|
32653262386565396335646431633534663031613536656561643434366464353335313239393464
|
||||||
65396336613465313035396134636438636134316236636566333031646666383939383264303437
|
36333866343063656533623432306231343065343530343638653739343731336131626532376566
|
||||||
38656337326233363866336363363161333236326137393465633935626165663136316661316131
|
37653833313233386538666464386232353466303035316232646339653533663831323261636330
|
||||||
66393764313130373766386235663134376239323037373365333730643336656532316434663265
|
32646166366633656631393762626236356633393130386165643264663437666433336138353938
|
||||||
35646664383338326366386536616339393938336539373762643839613763323434363037326330
|
37653866646364333134313366316366636132623764366562623932383239643265646461646434
|
||||||
66353738643735656165396138343262653563353934376237366564636638623630393238373530
|
63656233316639616134666635373434323030643136626238646637386634333439616231336136
|
||||||
39336335303564616232393165653639326166333536343966353933633365326539383632646639
|
38326431363062326232626238613334356633353362653138643237646339333762616330383934
|
||||||
37636238376137646239353731343933346562613630656130653865633834626363656231373162
|
63613235376530373834333834316539343330313862666262633439333062633261646138626434
|
||||||
65333662653962373139633466383834626566336234316161633038653137393266393564376439
|
33616162316632366537393939343163333530393939613936323164663034326536666133303365
|
||||||
62343365666637396639326438326631613664666662653935346230316632396361663038663239
|
63643337343037363363663235316330316131353064383933323131643036623862303835336633
|
||||||
63336335643665353162393330623635663839666162333561376265363330383530653733333239
|
39376134313264333834306330366136643434386566393466313835396466306363663565303065
|
||||||
37373665646562343562313764386534663239636130646564333433313661363338326439363865
|
33393431636163376365396139346162346239343838616561373162643931623365336436626636
|
||||||
63353064613161366461383862623363333637633430376361366566383332636634646630363139
|
39613030356162363931316166376231303033306366616162366239396333323838363465353261
|
||||||
65303139396539323336313666336362626339326666343061303431356361343438313365353431
|
64303561386563383834386166323739626237376164326533616533343338316430366365333966
|
||||||
30303266363664373233303231346166643839343564306363613236316665306437333636343131
|
31343233666365663830323030336161623466633261303637373537336333663262383238393833
|
||||||
61303234666664323138376335623639393939633836353664343232316630393263323233343366
|
33383033343630663237653338636436346565356238313434346666336436653835326133313038
|
||||||
32393736336166323166616139323066613062646139656236343233623630616262343034383631
|
65353530333839373136326132326439306431333334633933663065316531616263633533346431
|
||||||
61653662383537646364363764353335333630353431633534313731633265626263303863326565
|
62383862653162613532383136363830313964366161306166343934363865316533643736373431
|
||||||
37616534303164316162353165396236656565303532326235393237366434323739633662663261
|
61323135343030346336356232326138376564343131636263336332333239303733323338663830
|
||||||
33383635653734303233356438636132343731383439623463633732373833303339353662386435
|
32373639343063353261623831623237663133646535636338353333653237653539656438666436
|
||||||
63316138353766303962616435383138373739383835353662613666356565366237353932393263
|
63636537336636313762336531346531313637653834386535313666306636303338306465303764
|
||||||
34326561613234356537343234303166613534356134643766383332646263623430653632386365
|
63366133396263636164386339343131653361363831363932303734333634343734636163383539
|
||||||
61376165656366366532346465343932313032643163313362373633373066653036346238343938
|
33613430353236616132636431656636633561316161623661373663396462633930343865336236
|
||||||
66626133306163353966386662363039323166353330633239386264306337386361653430633838
|
32373534666135303730303663333337643033346231646661393137643161383833626335336561
|
||||||
66366636616661313939316562373330313564383033643035393738353939613066316138643238
|
66366433303436313132636230313531316261613564323963626438623530633634363265613939
|
||||||
35376336633238636463376234376364386464396461636336313562616361396435323639666433
|
37666636313632353831343238646237666464333039306630343531626339323562346162663130
|
||||||
34653139613962623235613035363634383735326564333332333632656237333238356566646536
|
65633133366564306230666436663630316434616634656661306461353866396662376331666630
|
||||||
62346435303932363231306634643037623935653235613330666666656433396463656231643364
|
30323166386535633361316633303830623031393531343532633930616439326465613631613838
|
||||||
34313235613064646432633935656231643162363530313761303339323830653564303963643532
|
38326630366534363036666433333237363636643366333161343336363936323730313339343132
|
||||||
39326163396461613161363031616336666564326630323465663831653431353566643661656435
|
35356231666633386537366236303136663261336532386338316565343239333261613838623861
|
||||||
35636238633663346139343732353233663930353664623636303134303633356232313063346566
|
31366531343232653431343436343766386331303133363534616536633337656433326265656161
|
||||||
30633762653239356131613539396435396235326431633062616436613266353266636133656531
|
66356235616135363634313835666134346232356663326361396537303339373334323861383161
|
||||||
63346533383764393733396436306136383830313265656539376136343536353737313365333865
|
33393438313061613465666536666636643166613463363165393338643066613231636138343537
|
||||||
61653665393037346231656663363432666332383837393264656231386435323333376633333265
|
64333731376139353934316631393561356163366237386337393061343235633131613962383731
|
||||||
39633563653161663531303330393237316132653339653531316538316534353334383161623833
|
66376261633832323864356564326532356363343762313563393331393933316336373434333237
|
||||||
63656534643833656366623031326132343732663536393636393537383338613462336638626334
|
35396531303330396363333163373361643837383766333730303031613665313237383532666264
|
||||||
39326264623032393661303335356235343736613964636261633038333236623232613633343263
|
65613237623136336239653566646366376538356362643062386436363466616331336665616432
|
||||||
34363865306233346433393664363038303464363834313463636363366333313433333930336634
|
31383365363630663530643764633335336332386332663661393733343038626265383432306630
|
||||||
65396232663031323965343336366339623239386566323362396564333932336134336231646430
|
38653861356636313634326266633637653764396233313463393964653739306234666662336432
|
||||||
34376236636536626264663532323530656634333137363431313339643164333236303337373639
|
33393139343864346233653763343762616230383131353166396265653031326232656333626264
|
||||||
31326131636161386665623838303939616337636666346363616465323263373061633832363839
|
63616339663564363665626338316661623230646534373231313662343736316462663763333364
|
||||||
39343131386333666231643931353330343136363732396465383336303866623733316235646234
|
30333137386161336664386536366538313934313039333832323763313264373861666239643466
|
||||||
65613531613333653231666234353532313738363266353336303564613061393639323933646339
|
33383334633736333634306262316265323634386635313764376532646364356565396538656137
|
||||||
64396334396431313563326136656539613535663334323330326566613264393965623065616634
|
63316232643832383865303934636364313036666237636632393137373933656263646534623230
|
||||||
32313333663535313531343363656230656334346430643365363838656430616366653766333136
|
65616231323035643531353430326630353761633234636234343834656664323161383335343235
|
||||||
32346137656136666432383934313865613535303962333464316465313464333233646162376165
|
62616533366465303961306134386265303933616236346332623536393236633366333634316264
|
||||||
37363265316139643332393761663164346537663064346133323661393030393533396633646134
|
31396361343833623131306266336238343866303361303336346566656639616134383063396235
|
||||||
39383539333364626263323364353764316539323162346434656234393562326133613632633137
|
31626633333665373934333961646263663730653331326137383031383736646633356536333431
|
||||||
34323537393838393534643861356365376461636235386638376661623439303832643231616431
|
39303632383030613465656236333763393737353865386235333830613665653363393536646630
|
||||||
32663264306166366336396130326435346432343732323764346232356439633361663937313233
|
65353733613536653963393831636530636231366266343262376631393062623163313031623230
|
||||||
62636130663238333433396335356331653166366130626632663162653736656238343134353134
|
62326561653165306339633439376461346330393231313366666339346536313765363163616630
|
||||||
39373663633437626661393734363264313137663138643665353633366663366562386337396561
|
35373564366563656161376336323031346339313734633139383563393966373637616266666265
|
||||||
38383464333531306138316266626461326531616364303732343337386464366464633834383439
|
34623330313265373935396130643231353131313163393333396337396666636439633035326635
|
||||||
66303334333234306166396235666164313833333164356561663063326563303934373933313334
|
32663762333537663839346531663132613636366639643364373333343262326264366136363331
|
||||||
32326466343761333666373936373332663939643662363665323534333638316637666665326564
|
61613833643639656632353931663830613634383334653036323462323262386265313637656535
|
||||||
63616134623530333739633265343637626561383831663836626538313539653536373165636133
|
36333834306438633532616335663562383730306337656336663035643136386261326130336363
|
||||||
62396432376332356133336461633664336264616566376664643861363838313766393833343366
|
32623330333764616565333162383234656463363432313039666265653563323232613930303765
|
||||||
31386636343835663062396232646663363935396366343361633462643734373437666331383035
|
30623834666665636332303862663832303537656664643362643636616265613739356664646462
|
||||||
30373963323834343138653063313635643062396130373861643134363062363135623730663335
|
61326466303266626166333730343330396439356663376362666536623539353666636230373533
|
||||||
34373935623239363866393932363966366233643732646461343964383563393064396331353331
|
35363461326263366137313037323166396133646430616664343165623533393963623535626237
|
||||||
31646662323364376639316562666465356438386637393130616330663563633839326661643239
|
39633362393636373131626364636437343361303435396138363735326235383237633964336138
|
||||||
62346633353963336239633738336431326638626232313965336361636430323739653734636436
|
31643935646431386537643733353862666138333238323461623638356535316365653530376464
|
||||||
39316363316137366233616431346639663335393737653562356532363634346237336266333132
|
33343465653134613536343563376564663133336532343330666131346663363434623238303862
|
||||||
31343836326538653164653163653738656238326366313532613433333337383263646439326661
|
31306331633238623836346466616230356532316232323734356638343532643032633937653061
|
||||||
65306135363138646665363463636436303864316432663734393130313932643833356161373139
|
37393265333233626563353963363637653338393964363832663734303566356633623733393164
|
||||||
65376334376136313237393336636630643933373165336432643233306366633663333930396364
|
64383536343862393134336638353733373262396563303266346535346266306238616531336162
|
||||||
31393739333730326434313639376365356432376338386430363133666436643837633533616333
|
35363638333431396163303530353461363338656363336366376430316464353131623661346366
|
||||||
36363464633233343130656163353137663165343064373039363439346631393436393430383361
|
35313930353538343830333337356163613765356361343663613933366464353935336361636436
|
||||||
61353563346531353561303935363531333235313731303237326334363763646131653961613334
|
65356465346535613231363932303339646162303238376236323461303062313336656366306563
|
||||||
33306534663238343433363237393234643236393931343230656438353866313638636633396464
|
31353464333539343839353764393665393235653537313939373563303436626635663766303336
|
||||||
61636561333562383964663261316135373235313234613564333333336438646636303561616634
|
35333838323734386537623334366235653534623664396664656264633735353634663332353364
|
||||||
63363033613238323463646165373361613834353230653138623132303731393436386139663533
|
65306132346464363966313963346633353538633936363164393566376630376365316139376132
|
||||||
38393263306139303231626363353931646163323364666161653861643739353262313561393165
|
38323039653465346462666439656134393964623563393664643634313638323832356164323863
|
||||||
6262
|
31366535336238663236666162336166313933376531643137343665653765623961333464633332
|
||||||
|
30306265306364643738386561303833326363303836333032393462643764663664613536363835
|
||||||
|
30663937333038663435326637663636383165363632326337653932336130333932333861366235
|
||||||
|
66313566613033383631376132333634653639336666386164613934353230613239383239313038
|
||||||
|
30653162643233636638393036353032353632316166333938313966626133643237376461313065
|
||||||
|
34323762376133393535613864636461616261343031393266666165316236323231323534376465
|
||||||
|
63386363343933316139356163363132343666366132313038643938653865663934383335336364
|
||||||
|
31646563346339323633643366653861323030636138663861636434306238623738636331393538
|
||||||
|
66333765626132346533313261646434633236633432333430613266333161643566326434363633
|
||||||
|
62373033356337393232353566666263353539326564333766303335666135343461386530626562
|
||||||
|
63633137623361366362616432613237353231616465656232373835376463386132663331633737
|
||||||
|
63306136326264643365356634336233303163663135366531643362303666653630363962666637
|
||||||
|
61626539613333306263323564373435613961633439633261373530363137376132326462633035
|
||||||
|
30353838336433313135623530643663643232653364316263356164633661383937393238363262
|
||||||
|
34393666663361613735623435626238646266376533333963653630663462653733373130393338
|
||||||
|
32626361366632633630323363376238343534626230373835353036333065396435653964326534
|
||||||
|
33356466643839626162316438356437623732656664613434613165643666656230656434633638
|
||||||
|
61316134333631326365313532313335633634356466663834666531653365333333616137643835
|
||||||
|
30666664353134313236653337366466623966366131313962346564663432633535363938333261
|
||||||
|
37353464383832373366613531666161323632616236613631653433643562326134623838393135
|
||||||
|
62323533373336663434386166333862366639626562343830396533646138636261326161393339
|
||||||
|
33613961353431653537373931396531623061653163376230663338376636613136353433646231
|
||||||
|
33633236306238336432323134393463653161303662336266313562316337343430373532663930
|
||||||
|
39613133646432343238393762386266303531663032306130653532376262353238636231393539
|
||||||
|
36376332633563633661353835386364633461363437333038363865356237643739323133653235
|
||||||
|
66373931336330626663383136363265363133393239393131623465383433363336333237653131
|
||||||
|
64656662316665356665303765343932363733666362353031656234373363663364666537663030
|
||||||
|
34646233386630633438313166396663383732366233373139343431373463346133316663643036
|
||||||
|
31633536663266383132393032313563653035303034336161356139396636353365636163383031
|
||||||
|
30666637363933643262393065633463313836653530663232303736306537636533343431333966
|
||||||
|
32386330356564613932306466353931393839316562353362303765663263383738363765333136
|
||||||
|
36383036376130306133393166333734316231376165393832383735623838353466353664353834
|
||||||
|
39326136656239626434313930306435333533623533643236393437313038393235386364323766
|
||||||
|
62323537666333613066356236313361376138656232343430363438346261646165623565313435
|
||||||
|
65326532623761343239666664636637613034623736386665303634613737613964333630613533
|
||||||
|
31653731353533613866663166653237663162623236353838646465373734613466616362363833
|
||||||
|
39346335373539376439643937363233373362616334323231623232353061343830383336666466
|
||||||
|
31303663643061653866326632333336626462623835366564356231316263326130393138306330
|
||||||
|
66333532336164633931306136373531666131356130313262313038626138653864633734636132
|
||||||
|
61353638333133383035333937626333376564316465333539653138323739356265613664653536
|
||||||
|
35656136343036363532613335336230373364336432343731326132666662636664356364303335
|
||||||
|
66613736633231393534626539333536626565663231396536363062353037346563643934313236
|
||||||
|
32363138386430633761383065343834376631643539626330313638333534333734333239383632
|
||||||
|
39383833656134393165666533353739303834633330653936643637316639663033363865653631
|
||||||
|
36313735613962393264623938336432336538393937316634626165383934356562353035316533
|
||||||
|
33353735653133636439393763356430303863326235623932623464356636666265653065383736
|
||||||
|
31613666303065653035303131646364386636393035663865336164376536323462666239356333
|
||||||
|
33373562383863323635613634643165336465613734613034366236633835363733306662613462
|
||||||
|
30326565346365633131363331643165346466366666633231643538303430376530393139393063
|
||||||
|
32666664313162303065616261326261353130623564343761633861373034646161643163373533
|
||||||
|
36386665626530313030343032376531656333356332666437383530613834356336386465353539
|
||||||
|
66623062646236333465316137316564363938303966613561643830353332333537373736626438
|
||||||
|
64653239613063646430386631313435306462303566333734663462626530356530663235303166
|
||||||
|
31633366613264316137303334613366366537646261303962646364643238383062643732386436
|
||||||
|
39643430613562663136633130666563396538306633633034323064386136303034306531323564
|
||||||
|
63646633303035353831653438366635646562613639313230396462373161363030386264623033
|
||||||
|
62343031616135346238303337313931643461386363396663333231626661613938626366653837
|
||||||
|
33303265656561313332353463363534666232363561353532643532386133306332333930373161
|
||||||
|
66626135303036633566303961646461383236663737643265346566393963343634623164633966
|
||||||
|
61313031333863383237313633663633333866366230313936396334383361393338656465346437
|
||||||
|
65613763643061386463386431386539656635393435633032343030633738373936613833623938
|
||||||
|
39316239363835633339343161623237656230633763313031643663623466323764663366376165
|
||||||
|
36366530363535376636383561343161643037626639323830396665303238663534633531613735
|
||||||
|
39316639633730376533643932633432353835353439646666653766666338646662643162373263
|
||||||
|
65356563303235633963633232393736636537346637376462626637303535383063373134613732
|
||||||
|
32663763663364633463633738343535316436303365343665356133313836616164343434613133
|
||||||
|
34626434643531343461346332636539636463313539393830613434333933643632313737356564
|
||||||
|
39386137383165616139626363613732626336366461663339346134656530396464323533313265
|
||||||
|
32346535356466383135636638616166313663613565353765346264376535653135356638663538
|
||||||
|
30623834393935636562656639643737373462643137363063663737306361336339653334633665
|
||||||
|
65316237633436303761393766393234326538653633363936303534646566373338623935666538
|
||||||
|
38313761643437313565663762613838636163633066313630353631353934396338353665653330
|
||||||
|
35393661356630313436373761383462396434643535316364383338343433613061353637333563
|
||||||
|
38376562373462316334313632343965326235323231653030666666616163346438626437386637
|
||||||
|
65656163653935656530366239663237633937336166613132356333623964666630386331383331
|
||||||
|
32303166386431643739303363646633376331623166313135653265656636663236363936336462
|
||||||
|
63303135613539663233366362323565396137623264353431373836356233666332633731666364
|
||||||
|
62376565633037373832393366306431613863353137306535393664313866616235633638306365
|
||||||
|
32633761386462386339343066316263663438623330373733333634363736623637636661636331
|
||||||
|
38353835333962633537633864616132643563616437336334326633323636393833363666353261
|
||||||
|
36363732383565623233343439666430303961306263363032383238653265323637313430613133
|
||||||
|
63633338323536386139613233343636326564356564353065386664646230666561336338366436
|
||||||
|
35353162633864633764373535366634303738346236633362626165393632383762343036313930
|
||||||
|
62363864626338393736373938343264356235393763653432306330363363313762333137303362
|
||||||
|
35393061356165666230343135663438303834363533323064626532663662323063386333623166
|
||||||
|
39353037363363373166316238393565353266353665666465333134303234373263663135346364
|
||||||
|
36646463343032326339663130393963663861323130356365396365643265313833353234323330
|
||||||
|
34336661363733613362343739366636336265356464613033343132353031663965656634613831
|
||||||
|
33656466376132656465383664363231653235623036346634666166623532323635313130303836
|
||||||
|
36393064356637623139653333633164666332303539623863383538386262363064356130353337
|
||||||
|
62393063633637383965346139626265666463356362633163363537303166336264346332636363
|
||||||
|
39613735626132366137356365303331633037623132623637383164636565653963626632626338
|
||||||
|
65383663633831613666396534333061643966313366303937333261303135353762656331336638
|
||||||
|
35643539326161646433633862623762626539396137303863616139383832613639653033653933
|
||||||
|
39373734393066613132323739383036313830346239306132656464646462626661633931623932
|
||||||
|
36626163353563633764323466303966636161646532316134383034393733653363363933353738
|
||||||
|
34383563666166323865656430346661633663363635623566336632353633303838303436616266
|
||||||
|
34326131623331616533306465373365396437303764383231356631313335393364643664663864
|
||||||
|
33313033303463376139376466643434383461643264316534306166303163373661643962343030
|
||||||
|
36653665353232363630363437376266306531633934626230653338383664343638313334306636
|
||||||
|
38666162623761366237383236346564333333633162623832656462616662313031316166383465
|
||||||
|
31663362336663353564636335393738333565356432306139363661656663313234396664623832
|
||||||
|
38393630666338663031323464383535303539643931393732616666366661316163626339643437
|
||||||
|
34316532613063353264303462366534613035653834356562646637633137643839653237353937
|
||||||
|
34326230396137383531393962346137643035333834376537363865643366623932303662303839
|
||||||
|
64373431306464363762356563663038343737356165313832613965653065326532356133646137
|
||||||
|
30326232643334656363343533383163643130343836366430333836396463386666623465366336
|
||||||
|
61343236303336623863303630336437613932353832623866633661653566623431653938306238
|
||||||
|
37326630616131646634383539353234323766363633333030613937616632333432366565343537
|
||||||
|
35613237343838333430656336313232653530373863663031386433356337643334303363666532
|
||||||
|
65633033333463366636616138646632636534333963643864383033343463666338373630313232
|
||||||
|
63326365666666663262383664353664623963343366626364363965626435666363306237346130
|
||||||
|
30306661386334653137336464393465646135353436336138616563366163366664346331646666
|
||||||
|
61613334323330663835366163623836363534666531353737656464663935333765326235306566
|
||||||
|
61353962363034663563386137373432656166663661653861396361653930653539363333613435
|
||||||
|
64373731616330396130363464653865303931333637393333386531303365646130646161366263
|
||||||
|
61643234333563386338643331303164323638346639353765656632386262366666376665356164
|
||||||
|
34393464656634303130323738353161643537613337383661343232363961613931613234643361
|
||||||
|
34663938626364636562613332373161323932386532356261353137393533633731653034633966
|
||||||
|
62316666613738383665303433376438303266646264353133303566636436373966343662363561
|
||||||
|
63333662613930323666656166373763303961333332363231396532376564393966333135373966
|
||||||
|
65623664303537376362383861663238663463396537353866666333656664323562373165646633
|
||||||
|
35396661353639356363613834653432346539633135663565376234383866343433383339666362
|
||||||
|
63373330336562323138343934646466373738333039346361623836653231633566316435383636
|
||||||
|
61326535393339616639366563383662636136353162393961303362393866363436663630303762
|
||||||
|
65343632396238623137333462633633653761336635663265326332623631393566323530623562
|
||||||
|
36666431396532303939316662663138356631336434373732393463306336303435626232316638
|
||||||
|
36363733383831363930666132396661633733616464393264343339333166633739346236383833
|
||||||
|
33373962313762323430343161616361636363366334613032323637316261656333633639333066
|
||||||
|
34653635393031323262353037396130383964363037633463653331653965363562326532623037
|
||||||
|
383331306234353238633435663136623634
|
||||||
|
|||||||
16
ansible/host_vars/carousel-of-progress/vars.yml
Normal file
16
ansible/host_vars/carousel-of-progress/vars.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# FILE: ansible/host_vars/astro_orbiter/vars.yml
|
||||||
|
# HOST: astro-orbiter (10.1.71.130)
|
||||||
|
# ROLE: Ollama inference host with AMD RX 5700 GPU passthrough
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ansible_host: 10.1.71.131
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
# LVM root expansion — xlarge template uses sda3 partition, standard VG/LV names
|
||||||
|
common_expand_root_lvm: true
|
||||||
|
common_root_pv: /dev/sda3
|
||||||
|
common_root_vg: ubuntu-vg
|
||||||
|
common_root_lv: ubuntu-lv
|
||||||
@@ -50,8 +50,9 @@ nextcloud_server:
|
|||||||
|
|
||||||
semaphore_server:
|
semaphore_server:
|
||||||
hosts:
|
hosts:
|
||||||
imagineering:
|
figment:
|
||||||
ansible_host: 10.1.71.37
|
ansible_host: 10.1.71.37
|
||||||
|
ansible_user: wed
|
||||||
ansible_become: true
|
ansible_become: true
|
||||||
|
|
||||||
n8n_server:
|
n8n_server:
|
||||||
@@ -65,6 +66,20 @@ ollama_server:
|
|||||||
ansible_user: wed
|
ansible_user: wed
|
||||||
ansible_become: true
|
ansible_become: true
|
||||||
|
|
||||||
|
hermes_server:
|
||||||
|
hosts:
|
||||||
|
carousel-of-progress:
|
||||||
|
ansible_host: 10.1.71.131
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
|
honcho_server:
|
||||||
|
hosts:
|
||||||
|
lincoln:
|
||||||
|
ansible_host: 10.1.71.132
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
papermc_server:
|
papermc_server:
|
||||||
# ansible-galaxy role install engonzal.papermc
|
# ansible-galaxy role install engonzal.papermc
|
||||||
hosts:
|
hosts:
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
#
|
#
|
||||||
# 1. Syncs boilerplates/traefik/dynamic/ to lightning-lane
|
# 1. Syncs boilerplates/traefik/dynamic/ to lightning-lane
|
||||||
# 2. Scans the directory for service configs
|
# 2. Scans the directory for service configs
|
||||||
# 3. Creates CNAME records for each service -> lightning-lane
|
# 3. Extracts all hostnames from Host() rules (supports multi-host)
|
||||||
|
# 4. Creates CNAME records for each hostname -> lightning-lane
|
||||||
#
|
#
|
||||||
# PREREQUISITES:
|
# PREREQUISITES:
|
||||||
# - Service dynamic config YAML committed to boilerplates/traefik/dynamic/
|
# - Service dynamic config YAML committed to boilerplates/traefik/dynamic/
|
||||||
@@ -14,15 +15,6 @@
|
|||||||
#
|
#
|
||||||
# USAGE:
|
# USAGE:
|
||||||
# ansible-playbook -i inventory.yml playbooks/add_service_route.yml
|
# ansible-playbook -i inventory.yml playbooks/add_service_route.yml
|
||||||
#
|
|
||||||
# ADDING A NEW SERVICE:
|
|
||||||
# 1. Create boilerplates/traefik/dynamic/<service>.yml
|
|
||||||
# 2. Commit and push
|
|
||||||
# 3. Run this playbook
|
|
||||||
#
|
|
||||||
# EXCLUDING FILES:
|
|
||||||
# Files that are not service routes (e.g., default.yml for middleware
|
|
||||||
# definitions) should be added to the exclude_configs list below.
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
- name: Sync Traefik routes and ensure DNS records
|
- name: Sync Traefik routes and ensure DNS records
|
||||||
@@ -33,13 +25,12 @@
|
|||||||
vars:
|
vars:
|
||||||
base_domain: "local.mk-labs.cloud"
|
base_domain: "local.mk-labs.cloud"
|
||||||
dns_server: "monorail"
|
dns_server: "monorail"
|
||||||
traefik_host: "10.1.71.35"
|
traefik_host: "lightning-lane.local.mk-labs.cloud"
|
||||||
traefik_user: "wed"
|
traefik_user: "wed"
|
||||||
traefik_dynamic_path: "/opt/docker/traefik/dynamic/"
|
traefik_dynamic_path: "/opt/docker/traefik/dynamic/"
|
||||||
dynamic_config_dir: "{{ playbook_dir }}/../../boilerplates/traefik/dynamic"
|
dynamic_config_dir: "{{ playbook_dir }}/../../boilerplates/traefik/dynamic"
|
||||||
|
|
||||||
# Files in the dynamic directory that are NOT service routes
|
# Files in the dynamic directory that are NOT service routes
|
||||||
# (middleware definitions, TLS options, etc.)
|
|
||||||
exclude_configs:
|
exclude_configs:
|
||||||
- default.yml
|
- default.yml
|
||||||
|
|
||||||
@@ -53,38 +44,52 @@
|
|||||||
register: sync_result
|
register: sync_result
|
||||||
changed_when: "'sending incremental file list' in sync_result.stdout"
|
changed_when: "'sending incremental file list' in sync_result.stdout"
|
||||||
|
|
||||||
# ── Step 2: Discover service configs ──
|
# ── Step 2: Discover hostnames from Traefik router rules ──
|
||||||
- name: Find all dynamic config files
|
- name: Find all dynamic config files
|
||||||
ansible.builtin.find:
|
ansible.builtin.find:
|
||||||
paths: "{{ dynamic_config_dir }}"
|
paths: "{{ dynamic_config_dir }}"
|
||||||
patterns: "*.yml"
|
patterns: "*.yml"
|
||||||
register: config_files
|
register: config_files
|
||||||
|
|
||||||
- name: Build service list from config filenames
|
- name: Read config files
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.slurp:
|
||||||
service_names: >-
|
src: "{{ item.path }}"
|
||||||
{{ config_files.files
|
register: slurped_configs
|
||||||
| map(attribute='path')
|
loop: "{{ config_files.files }}"
|
||||||
| map('basename')
|
when: item.path | basename not in exclude_configs
|
||||||
| reject('in', exclude_configs)
|
|
||||||
| map('regex_replace', '\.yml$', '')
|
|
||||||
| list }}
|
|
||||||
|
|
||||||
- name: Display services to route
|
- name: Extract all hostnames from Host() rules
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
hostnames: >-
|
||||||
|
{% set hosts = [] -%}
|
||||||
|
{% for result in slurped_configs.results if result.content is defined -%}
|
||||||
|
{% set content = result.content | b64decode -%}
|
||||||
|
{% for match in content | regex_findall('Host\(`([^`]+)`\)') -%}
|
||||||
|
{% for h in match.split(' || ') -%}
|
||||||
|
{% set h = h | regex_replace('`', '') | trim -%}
|
||||||
|
{% if h.endswith('.local.mk-labs.cloud') and h not in hosts -%}
|
||||||
|
{% set _ = hosts.append(h) -%}
|
||||||
|
{% endif -%}
|
||||||
|
{% endfor -%}
|
||||||
|
{% endfor -%}
|
||||||
|
{% endfor -%}
|
||||||
|
{{ hosts | unique | list }}
|
||||||
|
|
||||||
|
- name: Display hostnames to create
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg: "Services found: {{ service_names }}"
|
msg: "Hostnames found: {{ hostnames }}"
|
||||||
|
|
||||||
# ── Step 3: Create DNS CNAME records ──
|
# ── Step 3: Create DNS CNAME records ──
|
||||||
- name: Create DNS CNAME record for each service
|
- name: Create DNS CNAME record for each hostname
|
||||||
effectivelywild.technitium_dns.technitium_dns_add_record:
|
effectivelywild.technitium_dns.technitium_dns_add_record:
|
||||||
api_url: "http://{{ dns_server }}.{{ base_domain }}"
|
api_url: "http://{{ dns_server }}.{{ base_domain }}"
|
||||||
api_token: "{{ vault_technitium_api_key }}"
|
api_token: "{{ vault_technitium_api_key }}"
|
||||||
zone: "{{ base_domain }}"
|
zone: "{{ base_domain }}"
|
||||||
name: "{{ item }}.{{ base_domain }}"
|
name: "{{ item }}"
|
||||||
type: "CNAME"
|
type: "CNAME"
|
||||||
cname: "lightning-lane.{{ base_domain }}"
|
cname: "lightning-lane.{{ base_domain }}"
|
||||||
ttl: 360
|
ttl: 360
|
||||||
validate_certs: false
|
validate_certs: false
|
||||||
loop: "{{ service_names }}"
|
loop: "{{ hostnames }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item }}.{{ base_domain }}"
|
label: "{{ item }}"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Apply common role
|
- name: Apply day0 baseline
|
||||||
hosts: "{{ target | default('all') }}"
|
hosts: "{{ target | default('all') }}"
|
||||||
become: true
|
become: true
|
||||||
roles:
|
roles:
|
||||||
- common
|
- day0-baseline
|
||||||
32
ansible/playbooks/day0_expand_root_lv.yml
Normal file
32
ansible/playbooks/day0_expand_root_lv.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day0_expand_root_lv.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Reclaims unallocated PE on the root volume group, extending the root LV
|
||||||
|
# to fill the VG and resizing the underlying filesystem (ext4 or xfs).
|
||||||
|
#
|
||||||
|
# Belongs to the day0 host-provisioning lifecycle. The Ubuntu Server
|
||||||
|
# autoinstall template ships with the root LV at ~half the disk size by
|
||||||
|
# default; this playbook is the canonical one-shot fix-up for that.
|
||||||
|
#
|
||||||
|
# Idempotent and safe to re-run. Hosts without LVM are no-op'd cleanly.
|
||||||
|
#
|
||||||
|
# Opt-out: set `expand_root_lv_skip: true` in host_vars/<host>.yml for
|
||||||
|
# hosts where free PE should NOT be claimed by root (e.g. hosts with a
|
||||||
|
# planned second LV in the same VG for application data).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook playbooks/day0_expand_root_lv.yml
|
||||||
|
# ansible-playbook playbooks/day0_expand_root_lv.yml -e target=lincoln
|
||||||
|
# ansible-playbook playbooks/day0_expand_root_lv.yml -e target=honcho_server
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Expand root logical volume to fill VG
|
||||||
|
hosts: "{{ target | default('all') }}"
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
tasks:
|
||||||
|
- name: Apply expand_root_lv role unless host opts out
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: expand_root_lv
|
||||||
|
when: not (expand_root_lv_skip | default(false) | bool)
|
||||||
23
ansible/playbooks/day0_linux_baseline.yml
Normal file
23
ansible/playbooks/day0_linux_baseline.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day0_linux_baseline.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Applies the mk-labs Linux baseline (linux-baseline role) to one or more
|
||||||
|
# hosts. Idempotent and safe to re-run.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook playbooks/day0_linux_baseline.yml
|
||||||
|
# ansible-playbook playbooks/day0_linux_baseline.yml -e target=figment
|
||||||
|
# ansible-playbook playbooks/day0_linux_baseline.yml -e target=semaphore_server
|
||||||
|
#
|
||||||
|
# To trigger an opt-in full system upgrade:
|
||||||
|
# ansible-playbook playbooks/day0_linux_baseline.yml \
|
||||||
|
# -e target=figment -e 'baseline_features={"full_upgrade": true}'
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Apply mk-labs Linux baseline
|
||||||
|
hosts: "{{ target | default('all') }}"
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- linux-baseline
|
||||||
30
ansible/playbooks/day0_provision.yml
Normal file
30
ansible/playbooks/day0_provision.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day0_provision.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Umbrella day0 playbook. Runs the full host-provisioning lifecycle in
|
||||||
|
# the correct order against newly-built VMs, so the operator runs ONE
|
||||||
|
# command per new host rather than chaining day0 steps manually.
|
||||||
|
#
|
||||||
|
# Order matters:
|
||||||
|
# 1. linux-baseline — timezone, NTP, packages, SSH hardening, jarvis user
|
||||||
|
# 2. expand_root_lv — reclaim PE left unallocated by the Ubuntu
|
||||||
|
# autoinstall template default
|
||||||
|
#
|
||||||
|
# Idempotent: every step is safe to re-run. Suitable to apply periodically
|
||||||
|
# from Semaphore as a baseline-drift check.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook playbooks/day0_provision.yml -e target=lincoln
|
||||||
|
# ansible-playbook playbooks/day0_provision.yml -e target=honcho_server
|
||||||
|
#
|
||||||
|
# For finer control over a single phase, the constituent playbooks are:
|
||||||
|
# playbooks/day0_linux_baseline.yml
|
||||||
|
# playbooks/day0_expand_root_lv.yml
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Import day0 linux baseline
|
||||||
|
ansible.builtin.import_playbook: day0_linux_baseline.yml
|
||||||
|
|
||||||
|
- name: Import day0 expand root LV
|
||||||
|
ansible.builtin.import_playbook: day0_expand_root_lv.yml
|
||||||
79
ansible/playbooks/day1_deploy_hermes.yml
Normal file
79
ansible/playbooks/day1_deploy_hermes.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
# =============================================================================
|
||||||
|
# day1_deploy_hermes.yml
|
||||||
|
# Deploy Hermes Agent (Nous Research) on carousel-of-progress (10.1.71.131)
|
||||||
|
#
|
||||||
|
# FIRST-RUN WORKFLOW:
|
||||||
|
# 1. Run this playbook:
|
||||||
|
# ansible-playbook playbooks/day1_deploy_hermes.yml
|
||||||
|
#
|
||||||
|
# 2. SSH to the host and run the setup wizard as the hermes user:
|
||||||
|
# ssh wed@carousel-of-progress.local.mk-labs.cloud
|
||||||
|
# sudo -u hermes hermes setup
|
||||||
|
#
|
||||||
|
# 3. Once configured, start and verify the service:
|
||||||
|
# sudo systemctl start hermes
|
||||||
|
# sudo systemctl status hermes
|
||||||
|
# sudo journalctl -u hermes -f
|
||||||
|
#
|
||||||
|
# VARIABLES:
|
||||||
|
# hermes_skip_browser: true — set to skip Playwright/Chromium install
|
||||||
|
# (saves ~300MB if browser automation not needed)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- name: Deploy Hermes Agent on carousel-of-progress
|
||||||
|
hosts: carousel-of-progress
|
||||||
|
gather_facts: true
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Verify target is carousel-of-progress
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- inventory_hostname == "carousel-of-progress"
|
||||||
|
fail_msg: >
|
||||||
|
This playbook is scoped to carousel-of-progress only.
|
||||||
|
Got: {{ inventory_hostname }}
|
||||||
|
|
||||||
|
- name: Confirm OS is Ubuntu
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ansible_distribution == "Ubuntu"
|
||||||
|
fail_msg: >
|
||||||
|
This playbook requires Ubuntu. Found: {{ ansible_distribution }}.
|
||||||
|
(If running Fedora, swap apt tasks for dnf and adjust Playwright deps.)
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: hermes
|
||||||
|
vars:
|
||||||
|
hermes_skip_browser: false # set true to skip Chromium install
|
||||||
|
|
||||||
|
post_tasks:
|
||||||
|
- name: Verify hermes binary is accessible system-wide
|
||||||
|
ansible.builtin.command: hermes --version
|
||||||
|
register: hermes_version_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: hermes_version_check.rc != 0
|
||||||
|
|
||||||
|
- name: Print hermes version
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ hermes_version_check.stdout }}"
|
||||||
|
|
||||||
|
- name: Print post-install instructions
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "============================================================"
|
||||||
|
- "Hermes installed on carousel-of-progress (10.1.71.131)"
|
||||||
|
- "============================================================"
|
||||||
|
- "Next steps:"
|
||||||
|
- " 1. SSH to the host:"
|
||||||
|
- " ssh wed@carousel-of-progress.local.mk-labs.cloud"
|
||||||
|
- " 2. Run the setup wizard as the hermes user:"
|
||||||
|
- " sudo -u hermes hermes setup"
|
||||||
|
- " 3. After config, start the service:"
|
||||||
|
- " sudo systemctl start hermes"
|
||||||
|
- " 4. Verify:"
|
||||||
|
- " sudo systemctl status hermes"
|
||||||
|
- " sudo journalctl -u hermes -f"
|
||||||
|
- "============================================================"
|
||||||
|
- "Service is ENABLED but NOT STARTED — config required first."
|
||||||
|
- "============================================================"
|
||||||
18
ansible/playbooks/day1_deploy_honcho.yml
Normal file
18
ansible/playbooks/day1_deploy_honcho.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day1_deploy_honcho.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Deploys Honcho + pgvector PostgreSQL on the `lincoln` host. Assumes day0
|
||||||
|
# host provisioning (linux-baseline + expand_root_lv) is already complete.
|
||||||
|
#
|
||||||
|
# Run via:
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day0_provision.yml -e target=lincoln
|
||||||
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_honcho.yml
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy Honcho on lincoln
|
||||||
|
hosts: honcho_server
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- honcho
|
||||||
@@ -1,22 +1,17 @@
|
|||||||
---
|
---
|
||||||
# ------------------------------------------------------------------------------
|
# ============================================================================
|
||||||
# FILE: playbooks/day1_deploy_semaphore.yml
|
# day1_deploy_semaphore.yml
|
||||||
# DESCRIPTION: Deploys Semaphore on imagineering
|
# ----------------------------------------------------------------------------
|
||||||
# Runs: common → docker-host → semaphore
|
# Deploys SemaphoreUI + PostgreSQL on the imagineering host (figment).
|
||||||
|
# Run AFTER day0_linux_baseline.yml has been applied to the target.
|
||||||
#
|
#
|
||||||
# USAGE:
|
# Usage:
|
||||||
# ansible-playbook -i inventory.yml playbooks/day1_deploy_semaphore.yml
|
# ansible-playbook -i inventory.yml playbooks/day1_deploy_semaphore.yml
|
||||||
#
|
# ============================================================================
|
||||||
# SECRETS REQUIRED IN VAULT (group_vars/all/vault):
|
|
||||||
# vault_semaphore_database_password
|
|
||||||
# vault_semaphore_admin_password
|
|
||||||
# vault_semaphore_access_key_encryption
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
- name: Deploy Semaphore
|
- name: Deploy SemaphoreUI on imagineering
|
||||||
hosts: semaphore_server
|
hosts: semaphore_server
|
||||||
become: true
|
become: true
|
||||||
|
gather_facts: true
|
||||||
roles:
|
roles:
|
||||||
- docker-host
|
|
||||||
- semaphore
|
- semaphore
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
---
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# FILE: roles/common/tasks/main.yml
|
|
||||||
# DESCRIPTION: Baseline configuration applied to all managed Ubuntu hosts.
|
|
||||||
# Handles hostname, timezone, core packages, NTP, ansible user,
|
|
||||||
# and optional LVM root volume expansion.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
- name: Set hostname
|
|
||||||
hostname:
|
|
||||||
name: "{{ inventory_hostname | replace('_', '-') }}"
|
|
||||||
|
|
||||||
- name: Set timezone
|
|
||||||
timezone:
|
|
||||||
name: "{{ common_timezone }}"
|
|
||||||
|
|
||||||
- name: Update apt cache
|
|
||||||
apt:
|
|
||||||
update_cache: yes
|
|
||||||
cache_valid_time: 3600
|
|
||||||
|
|
||||||
- name: Install base utility packages
|
|
||||||
apt:
|
|
||||||
name: "{{ common_packages }}"
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Install chrony
|
|
||||||
apt:
|
|
||||||
name: chrony
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Configure chrony to use sundial
|
|
||||||
template:
|
|
||||||
src: chrony.conf.j2
|
|
||||||
dest: /etc/chrony/chrony.conf
|
|
||||||
mode: '0644'
|
|
||||||
notify: restart chrony
|
|
||||||
|
|
||||||
- name: Ensure chrony is enabled and running
|
|
||||||
systemd:
|
|
||||||
name: chrony
|
|
||||||
state: started
|
|
||||||
enabled: yes
|
|
||||||
|
|
||||||
- name: Ensure ansible user has passwordless sudo
|
|
||||||
lineinfile:
|
|
||||||
path: /etc/sudoers.d/{{ ansible_user }}
|
|
||||||
line: "{{ ansible_user }} ALL=(ALL) NOPASSWD:ALL"
|
|
||||||
create: yes
|
|
||||||
mode: '0440'
|
|
||||||
validate: 'visudo -cf %s'
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# LVM root volume expansion
|
|
||||||
# Extends the root PV to the full disk size and grows the LV + filesystem.
|
|
||||||
# This codifies what was previously done manually after provisioning.
|
|
||||||
# Runs only when common_expand_root_lvm is true (default: true).
|
|
||||||
# Safe to re-run — pvresize and lvextend are idempotent when already at max.
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
- name: Expand root PV to full disk size
|
|
||||||
command: pvresize {{ common_root_pv }}
|
|
||||||
register: pvresize_result
|
|
||||||
changed_when: "'changed' in pvresize_result.stdout or pvresize_result.rc == 0"
|
|
||||||
when: common_expand_root_lvm | bool
|
|
||||||
|
|
||||||
- name: Extend root LV to 100% of free VG space
|
|
||||||
lvol:
|
|
||||||
vg: "{{ common_root_vg }}"
|
|
||||||
lv: "{{ common_root_lv }}"
|
|
||||||
size: +100%FREE
|
|
||||||
resizefs: yes
|
|
||||||
when:
|
|
||||||
- common_expand_root_lvm | bool
|
|
||||||
ignore_errors: yes
|
|
||||||
# ignore_errors because lvextend returns non-zero when already at max size.
|
|
||||||
# resizefs: yes handles the resize2fs call inline — no separate task needed.
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
# file: roles/semaphore/defaults/main.yml
|
|
||||||
|
|
||||||
semaphore_compose_dir: /opt/docker/semaphore
|
|
||||||
semaphore_ssh_key_file: "~/.ssh/ansible"
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
# file: roles/semaphore/handlers/main.yml
|
|
||||||
|
|
||||||
- name: Restart Semaphore
|
|
||||||
community.docker.docker_compose_v2:
|
|
||||||
project_src: "{{ semaphore_compose_dir }}"
|
|
||||||
state: restarted
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
---
|
|
||||||
# file: roles/semaphore/tasks/main.yml
|
|
||||||
|
|
||||||
- name: Create Semaphore directory
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ semaphore_compose_dir }}"
|
|
||||||
state: directory
|
|
||||||
owner: wed
|
|
||||||
group: docker
|
|
||||||
mode: '0755'
|
|
||||||
|
|
||||||
- name: Copy Compose file from boilerplate
|
|
||||||
ansible.builtin.copy:
|
|
||||||
src: "{{ playbook_dir }}/../../boilerplates/semaphore/compose.yaml"
|
|
||||||
dest: "{{ semaphore_compose_dir }}/compose.yaml"
|
|
||||||
owner: wed
|
|
||||||
group: docker
|
|
||||||
mode: '0644'
|
|
||||||
|
|
||||||
- name: Deploy Semaphore .env file
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: env.j2
|
|
||||||
dest: "{{ semaphore_compose_dir }}/.env"
|
|
||||||
owner: wed
|
|
||||||
group: docker
|
|
||||||
mode: '0600'
|
|
||||||
|
|
||||||
- name: Copy SSH key for Ansible authentication
|
|
||||||
ansible.builtin.copy:
|
|
||||||
src: "{{ semaphore_ssh_key_file }}"
|
|
||||||
dest: "{{ semaphore_compose_dir }}/ansible_key"
|
|
||||||
owner: "1001"
|
|
||||||
group: "1001"
|
|
||||||
mode: '0600'
|
|
||||||
|
|
||||||
- name: Start Semaphore containers
|
|
||||||
community.docker.docker_compose_v2:
|
|
||||||
project_src: "{{ semaphore_compose_dir }}"
|
|
||||||
state: present
|
|
||||||
register: semaphore_compose
|
|
||||||
|
|
||||||
- name: Wait for Semaphore to be ready
|
|
||||||
ansible.builtin.uri:
|
|
||||||
url: "http://localhost:3000/api/ping"
|
|
||||||
method: GET
|
|
||||||
status_code: 200
|
|
||||||
register: semaphore_health
|
|
||||||
retries: 48
|
|
||||||
delay: 5
|
|
||||||
until: semaphore_health.status == 200
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
DATABASE_PASSWORD={{ vault_semaphore_database_password }}
|
|
||||||
SEMAPHORE_ADMIN_PASSWORD={{ vault_semaphore_admin_password }}
|
|
||||||
SEMAPHORE_ACCESS_KEY_ENCRYPTION={{ vault_semaphore_access_key_encryption }}
|
|
||||||
@@ -1,21 +1,6 @@
|
|||||||
# requirements.yml
|
|
||||||
---
|
---
|
||||||
collections:
|
collections:
|
||||||
- name: community.general
|
- name: containers.podman
|
||||||
version: 11.4.1
|
version: ">=1.10.0"
|
||||||
|
|
||||||
# - name: nccurry.openshift
|
|
||||||
# version: 1.4.0
|
|
||||||
|
|
||||||
- name: somaz94.ansible_k8s_iac_tool
|
|
||||||
version: 1.1.6
|
|
||||||
|
|
||||||
# - name: prometheus.prometheus
|
|
||||||
# version: 0.27.0
|
|
||||||
|
|
||||||
- name: kubernetes.core
|
|
||||||
|
|
||||||
- name: community.proxmox
|
|
||||||
version: 1.4.0
|
|
||||||
|
|
||||||
- name: effectivelywild.technitium_dns
|
- name: effectivelywild.technitium_dns
|
||||||
|
version: ">=1.1.0"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ common_timezone: America/Chicago
|
|||||||
common_ntp_server: "sundial.local.mk-labs.cloud"
|
common_ntp_server: "sundial.local.mk-labs.cloud"
|
||||||
|
|
||||||
common_packages:
|
common_packages:
|
||||||
|
- acl
|
||||||
- curl
|
- curl
|
||||||
- wget
|
- wget
|
||||||
- vim
|
- vim
|
||||||
21
ansible/roles/common/tasks/main.yml
Normal file
21
ansible/roles/common/tasks/main.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
- name: Create cast group
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: cast
|
||||||
|
state: present
|
||||||
|
system: true
|
||||||
|
|
||||||
|
- name: Create cast user
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: cast
|
||||||
|
group: cast
|
||||||
|
shell: /bin/bash
|
||||||
|
create_home: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Ensure cast user has passwordless sudo
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/sudoers.d/cast
|
||||||
|
line: "cast ALL=(ALL) NOPASSWD:ALL"
|
||||||
|
create: yes
|
||||||
|
mode: '0440'
|
||||||
|
validate: 'visudo -cf %s'
|
||||||
12
ansible/roles/day0-baseline/defaults/main.yml
Normal file
12
ansible/roles/day0-baseline/defaults/main.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
# Timezone
|
||||||
|
timezone: "UTC"
|
||||||
|
|
||||||
|
# Packages to ensure are present
|
||||||
|
baseline_packages:
|
||||||
|
- chrony
|
||||||
|
- vim
|
||||||
|
- htop
|
||||||
|
- curl
|
||||||
|
- wget
|
||||||
|
- rsync
|
||||||
6
ansible/roles/day0-baseline/handlers/main.yml
Normal file
6
ansible/roles/day0-baseline/handlers/main.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: Restart sshd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: sshd
|
||||||
|
state: restarted
|
||||||
|
become: true
|
||||||
32
ansible/roles/day0-baseline/tasks/main.yml
Normal file
32
ansible/roles/day0-baseline/tasks/main.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
- name: Set timezone
|
||||||
|
community.general.timezone:
|
||||||
|
name: "{{ timezone }}"
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Enable and start chronyd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: chronyd
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Update all packages
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "*"
|
||||||
|
state: latest
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Install baseline packages
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "{{ baseline_packages }}"
|
||||||
|
state: present
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Disable root SSH login
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^PermitRootLogin'
|
||||||
|
line: "PermitRootLogin no"
|
||||||
|
become: true
|
||||||
|
notify: Restart sshd
|
||||||
70
ansible/roles/expand_root_lv/README.md
Normal file
70
ansible/roles/expand_root_lv/README.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# expand-root-lv role
|
||||||
|
|
||||||
|
Idempotent role that extends the root LVM logical volume to fill its
|
||||||
|
volume group and grows the underlying filesystem (ext4 or xfs).
|
||||||
|
|
||||||
|
## Where this runs in the lifecycle
|
||||||
|
|
||||||
|
Part of the **day0** host-provisioning lifecycle. The canonical entry
|
||||||
|
points are:
|
||||||
|
|
||||||
|
```
|
||||||
|
playbooks/day0_expand_root_lv.yml # standalone
|
||||||
|
playbooks/day0_provision.yml # umbrella (baseline + expand_root_lv)
|
||||||
|
```
|
||||||
|
|
||||||
|
Day1 application-deploy playbooks should NOT include this role —
|
||||||
|
day0 is assumed complete before day1 begins.
|
||||||
|
|
||||||
|
## Why this role exists
|
||||||
|
|
||||||
|
The Ubuntu Server autoinstall template (used by the mk-labs `wed`-baked
|
||||||
|
VM templates) provisions the root LV at roughly half the available disk
|
||||||
|
size — a longstanding installer default that surprises every operator
|
||||||
|
who hasn't been bitten by it before. ~90% of mk-labs VMs need this
|
||||||
|
fix-up before they're fully useful.
|
||||||
|
|
||||||
|
## Idempotency
|
||||||
|
|
||||||
|
- If `vg_free_count == 0`, the `lvextend` step is skipped and the
|
||||||
|
filesystem-grow step is also skipped (nothing to resize against).
|
||||||
|
- If the target volume group doesn't exist on the host (e.g. a non-LVM
|
||||||
|
layout), the role exits cleanly via `meta: end_play`.
|
||||||
|
- Safe to leave in a recurring playbook so future disk expansions
|
||||||
|
(Proxmox-side disk grow → reboot → run role) are picked up
|
||||||
|
automatically.
|
||||||
|
|
||||||
|
## Opt-out for multi-LV hosts
|
||||||
|
|
||||||
|
If a host will have a **second logical volume in the same VG** (e.g. a
|
||||||
|
dedicated `/var/lib/postgresql` LV for a database server), this role's
|
||||||
|
"grow root to fill VG" behavior is wrong — it will consume the free PE
|
||||||
|
that was being reserved for the second LV.
|
||||||
|
|
||||||
|
Set in `host_vars/<host>.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
expand_root_lv_skip: true
|
||||||
|
```
|
||||||
|
|
||||||
|
The day0 playbook checks this flag and skips the role cleanly.
|
||||||
|
|
||||||
|
## Defaults
|
||||||
|
|
||||||
|
| Variable | Default | Purpose |
|
||||||
|
|-------------------------------|---------------|-----------------------------------------------------|
|
||||||
|
| `expand_root_lv_vg_name` | `ubuntu-vg` | LVM volume group name (Ubuntu installer default). |
|
||||||
|
| `expand_root_lv_lv_name` | `ubuntu-lv` | LVM logical volume name (Ubuntu installer default). |
|
||||||
|
| `expand_root_lv_mountpoint` | `/` | Mountpoint of the filesystem to grow. |
|
||||||
|
|
||||||
|
Override the VG/LV names in `host_vars/<host>.yml` for hosts that use a
|
||||||
|
different LVM layout.
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
- Does not extend the underlying partition. If the operator grows the
|
||||||
|
Proxmox disk and the partition itself needs to grow before lvextend
|
||||||
|
can claim the new space, run `growpart /dev/sda 3` (or equivalent)
|
||||||
|
first. A future enhancement could automate this via `cloud-utils`'
|
||||||
|
`growpart` package, but it's out of scope for the initial template
|
||||||
|
fix-up case where the partition already covers the whole disk.
|
||||||
26
ansible/roles/expand_root_lv/defaults/main.yml
Normal file
26
ansible/roles/expand_root_lv/defaults/main.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# expand-root-lv role defaults
|
||||||
|
# ============================================================================
|
||||||
|
# Extends the root LVM logical volume to fill its volume group, then grows
|
||||||
|
# the underlying filesystem to match. Idempotent: when there's no free PE
|
||||||
|
# in the VG (i.e. the LV already fills the VG), the lvextend step is a
|
||||||
|
# no-op and resize2fs/xfs_growfs simply confirms the filesystem is at
|
||||||
|
# capacity.
|
||||||
|
#
|
||||||
|
# Designed for Ubuntu cloud-image-style installations where the autoinstall
|
||||||
|
# template provisions an LV at half the disk size (the Ubuntu Server
|
||||||
|
# installer's longstanding default). Run once after VM provisioning to
|
||||||
|
# reclaim the unallocated PE; safe to leave in a day1 playbook so future
|
||||||
|
# disk expansions are picked up automatically.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# The LV and VG names follow the Ubuntu Server installer's convention.
|
||||||
|
# Override per-host if your template differs.
|
||||||
|
expand_root_lv_vg_name: ubuntu-vg
|
||||||
|
expand_root_lv_lv_name: ubuntu-lv
|
||||||
|
|
||||||
|
# Mount point we expect to be backed by the target LV. Used purely for
|
||||||
|
# the resize2fs / xfs_growfs decision — the role inspects this path's
|
||||||
|
# filesystem type and dispatches to the correct grow command.
|
||||||
|
expand_root_lv_mountpoint: /
|
||||||
23
ansible/roles/expand_root_lv/meta/main.yml
Normal file
23
ansible/roles/expand_root_lv/meta/main.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
galaxy_info:
|
||||||
|
role_name: expand_root_lv
|
||||||
|
author: JARVIS
|
||||||
|
description: >-
|
||||||
|
Idempotent role that extends the root LVM logical volume to fill its
|
||||||
|
volume group and grows the underlying filesystem (ext4 or xfs). Fixes
|
||||||
|
the half-disk LV that the Ubuntu Server autoinstall template ships
|
||||||
|
with by default.
|
||||||
|
license: MIT
|
||||||
|
min_ansible_version: "2.14"
|
||||||
|
platforms:
|
||||||
|
- name: Ubuntu
|
||||||
|
versions:
|
||||||
|
- noble
|
||||||
|
- jammy
|
||||||
|
galaxy_tags:
|
||||||
|
- lvm
|
||||||
|
- cloud-init
|
||||||
|
- homelab
|
||||||
|
- storage
|
||||||
|
|
||||||
|
dependencies: []
|
||||||
70
ansible/roles/expand_root_lv/tasks/main.yml
Normal file
70
ansible/roles/expand_root_lv/tasks/main.yml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# expand-root-lv / main
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# 1. Confirm the target VG exists (skip role cleanly on non-LVM hosts).
|
||||||
|
# 2. Read free physical-extent count for the VG.
|
||||||
|
# 3. Extend the LV to +100%FREE only when free_pe > 0.
|
||||||
|
# 4. Grow the filesystem on the mountpoint (ext4 -> resize2fs, xfs -> xfs_growfs).
|
||||||
|
# Each step is idempotent and skips when there's nothing to do.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Gather LVM facts
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "vgs --noheadings --nosuffix --units b -o vg_name,vg_free_count {{ expand_root_lv_vg_name }}"
|
||||||
|
register: vg_info
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Skip role when target VG is absent
|
||||||
|
ansible.builtin.meta: end_play
|
||||||
|
when: vg_info.rc != 0
|
||||||
|
|
||||||
|
- name: Parse free PE count
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
expand_root_lv_free_pe: "{{ (vg_info.stdout.split() | last | int) if vg_info.stdout | length > 0 else 0 }}"
|
||||||
|
|
||||||
|
- name: Extend LV to fill VG (only if free PE > 0)
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "lvextend -l +100%FREE /dev/{{ expand_root_lv_vg_name }}/{{ expand_root_lv_lv_name }}"
|
||||||
|
register: lvextend_result
|
||||||
|
when: expand_root_lv_free_pe | int > 0
|
||||||
|
changed_when: lvextend_result.rc == 0
|
||||||
|
|
||||||
|
- name: Detect filesystem type at mountpoint
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "findmnt {{ expand_root_lv_mountpoint }} -no FSTYPE"
|
||||||
|
register: fstype_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set filesystem type fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
expand_root_lv_fstype: "{{ fstype_result.stdout | trim }}"
|
||||||
|
|
||||||
|
- name: Grow ext4 filesystem
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "resize2fs /dev/{{ expand_root_lv_vg_name }}/{{ expand_root_lv_lv_name }}"
|
||||||
|
register: resize_result
|
||||||
|
when:
|
||||||
|
- expand_root_lv_fstype == "ext4"
|
||||||
|
- lvextend_result.changed | default(false)
|
||||||
|
changed_when: resize_result.rc == 0
|
||||||
|
|
||||||
|
- name: Grow xfs filesystem
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "xfs_growfs {{ expand_root_lv_mountpoint }}"
|
||||||
|
register: xfs_result
|
||||||
|
when:
|
||||||
|
- expand_root_lv_fstype == "xfs"
|
||||||
|
- lvextend_result.changed | default(false)
|
||||||
|
changed_when: xfs_result.rc == 0
|
||||||
|
|
||||||
|
- name: Report current root size
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "df -h {{ expand_root_lv_mountpoint }}"
|
||||||
|
register: df_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Show post-resize disk usage
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ df_result.stdout_lines }}"
|
||||||
26
ansible/roles/hermes/defaults/main.yml
Normal file
26
ansible/roles/hermes/defaults/main.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
# Hermes service user
|
||||||
|
hermes_user: hermes
|
||||||
|
hermes_group: hermes
|
||||||
|
hermes_home: /home/hermes
|
||||||
|
|
||||||
|
# Install flags
|
||||||
|
# Set to true if you don't need browser automation (skips Playwright/Chromium)
|
||||||
|
hermes_skip_browser: false
|
||||||
|
|
||||||
|
# systemd service name (gateway)
|
||||||
|
hermes_service_name: hermes
|
||||||
|
|
||||||
|
# Path where hermes binary will be accessible system-wide
|
||||||
|
hermes_bin_symlink: /usr/local/bin/hermes
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Hermes Web UI (dashboard) — fronted by Traefik on lightning-lane via the
|
||||||
|
# 'jarvis' service. Binds to 0.0.0.0 so Traefik can reach it from upstream;
|
||||||
|
# --insecure is acceptable because TLS + auth are terminated at Traefik.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
hermes_dashboard_enabled: true
|
||||||
|
hermes_dashboard_service_name: hermes-dashboard
|
||||||
|
hermes_dashboard_host: 0.0.0.0
|
||||||
|
hermes_dashboard_port: 9119
|
||||||
|
hermes_dashboard_insecure: true
|
||||||
9
ansible/roles/hermes/handlers/main.yml
Normal file
9
ansible/roles/hermes/handlers/main.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
- name: reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: restart hermes
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ hermes_service_name }}"
|
||||||
|
state: restarted
|
||||||
199
ansible/roles/hermes/tasks/main.yml
Normal file
199
ansible/roles/hermes/tasks/main.yml
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
---
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 1. System prerequisites (run as root via become)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Install system packages required by Hermes installer
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- git
|
||||||
|
- curl
|
||||||
|
- ffmpeg
|
||||||
|
- ripgrep
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Install Node.js 22 (required for browser automation and WhatsApp bridge)
|
||||||
|
block:
|
||||||
|
- name: Download NodeSource setup script
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: https://deb.nodesource.com/setup_22.x
|
||||||
|
dest: /tmp/nodesource_setup.sh
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Run NodeSource setup script
|
||||||
|
ansible.builtin.command: bash /tmp/nodesource_setup.sh
|
||||||
|
args:
|
||||||
|
creates: /etc/apt/sources.list.d/nodesource.list
|
||||||
|
|
||||||
|
- name: Install nodejs
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: nodejs
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 2. Install Playwright system deps for Chromium (root-only step)
|
||||||
|
# Per docs: this is the one thing that genuinely needs root.
|
||||||
|
# Skipped entirely if hermes_skip_browser is true.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Install Playwright Chromium system dependencies
|
||||||
|
ansible.builtin.command: npx --yes playwright install-deps chromium
|
||||||
|
become: true
|
||||||
|
when: not hermes_skip_browser
|
||||||
|
changed_when: true
|
||||||
|
environment:
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 3. Create dedicated hermes service user
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Create hermes group
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: "{{ hermes_group }}"
|
||||||
|
state: present
|
||||||
|
system: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Create hermes user
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ hermes_user }}"
|
||||||
|
group: "{{ hermes_group }}"
|
||||||
|
home: "{{ hermes_home }}"
|
||||||
|
shell: /bin/bash
|
||||||
|
system: true
|
||||||
|
create_home: true
|
||||||
|
comment: "Hermes Agent service account"
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Grant hermes user passwordless sudo
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: "hermes ALL=(ALL) NOPASSWD:ALL\n"
|
||||||
|
dest: /etc/sudoers.d/hermes
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0440"
|
||||||
|
validate: /usr/sbin/visudo -cf %s
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Ensure hermes home directory has correct permissions
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ hermes_home }}"
|
||||||
|
owner: "{{ hermes_user }}"
|
||||||
|
group: "{{ hermes_group }}"
|
||||||
|
mode: "0750"
|
||||||
|
state: directory
|
||||||
|
become: true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 4. Run the Hermes installer as the hermes user
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Check if hermes is already installed
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ hermes_home }}/.hermes/hermes-agent/venv/bin/hermes"
|
||||||
|
register: hermes_binary
|
||||||
|
|
||||||
|
- name: Run Hermes installer as hermes user
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
|
||||||
|
| bash -s -- --skip-setup{% if hermes_skip_browser %} --skip-browser{% endif %}
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
become: true
|
||||||
|
become_user: "{{ hermes_user }}"
|
||||||
|
environment:
|
||||||
|
HOME: "{{ hermes_home }}"
|
||||||
|
PATH: "{{ hermes_home }}/.local/bin:/usr/local/bin:/usr/bin:/bin"
|
||||||
|
when: not hermes_binary.stat.exists
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 5. Symlink hermes binary into system PATH
|
||||||
|
# Per docs: service accounts often lack ~/.local/bin in PATH; symlink
|
||||||
|
# into /usr/local/bin so hermes is always accessible.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Symlink hermes binary to system PATH
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: "{{ hermes_home }}/.hermes/hermes-agent/venv/bin/hermes"
|
||||||
|
dest: "{{ hermes_bin_symlink }}"
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 6. Install systemd service unit
|
||||||
|
# NOTE: The service starts in 'gateway' mode for persistent operation.
|
||||||
|
# You must run 'sudo -u hermes hermes setup' interactively on first boot
|
||||||
|
# to configure your LLM provider and any messaging gateways before
|
||||||
|
# enabling the service.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy hermes systemd service unit
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: hermes.service.j2
|
||||||
|
dest: /etc/systemd/system/{{ hermes_service_name }}.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
become: true
|
||||||
|
notify:
|
||||||
|
- reload systemd
|
||||||
|
|
||||||
|
- name: Flush handlers to reload systemd now
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
|
||||||
|
- name: Enable hermes service (but do not start — config required first)
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ hermes_service_name }}"
|
||||||
|
enabled: true
|
||||||
|
state: stopped
|
||||||
|
become: true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 7. Install systemd service unit for the Hermes Web UI (dashboard)
|
||||||
|
# Separate unit from the gateway so the UI can be restarted / disabled
|
||||||
|
# independently. Fronted upstream by Traefik (jarvis service) on
|
||||||
|
# lightning-lane, so binding 0.0.0.0 with --insecure is intentional.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Deploy hermes-dashboard systemd service unit
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: hermes-dashboard.service.j2
|
||||||
|
dest: /etc/systemd/system/{{ hermes_dashboard_service_name }}.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
become: true
|
||||||
|
register: hermes_dashboard_unit
|
||||||
|
when: hermes_dashboard_enabled
|
||||||
|
|
||||||
|
- name: Reload systemd to pick up hermes-dashboard unit changes
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
become: true
|
||||||
|
when:
|
||||||
|
- hermes_dashboard_enabled
|
||||||
|
- hermes_dashboard_unit is changed
|
||||||
|
|
||||||
|
- name: Enable and start hermes-dashboard service
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ hermes_dashboard_service_name }}"
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
become: true
|
||||||
|
when: hermes_dashboard_enabled
|
||||||
|
|
||||||
|
- name: Restart hermes-dashboard on unit change
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ hermes_dashboard_service_name }}"
|
||||||
|
state: restarted
|
||||||
|
become: true
|
||||||
|
when:
|
||||||
|
- hermes_dashboard_enabled
|
||||||
|
- hermes_dashboard_unit is changed
|
||||||
24
ansible/roles/hermes/templates/hermes-dashboard.service.j2
Normal file
24
ansible/roles/hermes/templates/hermes-dashboard.service.j2
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Hermes Dashboard (Web UI)
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User={{ hermes_user }}
|
||||||
|
Group={{ hermes_group }}
|
||||||
|
WorkingDirectory={{ hermes_home }}
|
||||||
|
Environment="HOME={{ hermes_home }}"
|
||||||
|
Environment="PATH={{ hermes_home }}/.local/bin:/usr/local/bin:/usr/bin:/bin"
|
||||||
|
Environment="HERMES_HOME={{ hermes_home }}/.hermes"
|
||||||
|
ExecStart={{ hermes_home }}/.hermes/hermes-agent/venv/bin/hermes dashboard --host {{ hermes_dashboard_host }} --port {{ hermes_dashboard_port }}{% if hermes_dashboard_insecure %} --insecure{% endif %}
|
||||||
|
|
||||||
|
TimeoutStopSec=30
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=hermes-dashboard
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
23
ansible/roles/hermes/templates/hermes.service.j2
Normal file
23
ansible/roles/hermes/templates/hermes.service.j2
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Hermes Agent (Nous Research)
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User={{ hermes_user }}
|
||||||
|
Group={{ hermes_group }}
|
||||||
|
WorkingDirectory={{ hermes_home }}
|
||||||
|
Environment="HOME={{ hermes_home }}"
|
||||||
|
Environment="PATH={{ hermes_home }}/.local/bin:/usr/local/bin:/usr/bin:/bin"
|
||||||
|
Environment="HERMES_HOME={{ hermes_home }}/.hermes"
|
||||||
|
ExecStart={{ hermes_home }}/.hermes/hermes-agent/venv/bin/hermes gateway
|
||||||
|
TimeoutStopSec=200
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=hermes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
118
ansible/roles/honcho/README.md
Normal file
118
ansible/roles/honcho/README.md
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# Honcho role
|
||||||
|
|
||||||
|
Deploys [Honcho](https://honcho.dev) — Plastic Labs' memory + theory-of-mind
|
||||||
|
layer for stateful agents — on a single mk-labs VM (`lincoln`) using
|
||||||
|
rootful Podman + Quadlet.
|
||||||
|
|
||||||
|
## Topology
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────┐
|
||||||
|
│ lightning-lane (Traefik) │
|
||||||
|
│ hall-of-presidents.l... │
|
||||||
|
└─────────────┬────────────────┘
|
||||||
|
│
|
||||||
|
│ HTTP :8000
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────── lincoln ───────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ ┌───────────────┐ ┌───────────────┐ ┌──────────────────────┐ │
|
||||||
|
│ │ honcho-api │ │ honcho-deriver│ │ honcho-postgres │ │
|
||||||
|
│ │ (FastAPI:8000)│◄──►│ (worker loop) │◄──►│ pgvector/pgvector:pg16│ │
|
||||||
|
│ └──────┬────────┘ └──────┬────────┘ └──────────────────────┘ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └────────┬───────────┘ │
|
||||||
|
│ ▼ │
|
||||||
|
│ Anthropic Claude API (outbound, east-west to Internet) │
|
||||||
|
│ │
|
||||||
|
└────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## What this role does
|
||||||
|
|
||||||
|
1. Installs Podman + ensures the rootful Quadlet drop-in dir exists.
|
||||||
|
2. Creates a user-defined Podman network (`honcho-net`).
|
||||||
|
3. Creates a named volume for Postgres data (`honcho_postgres_data`).
|
||||||
|
4. Deploys a pgvector-enabled Postgres 16 container and waits for it to be
|
||||||
|
ready.
|
||||||
|
5. Deploys the Honcho API container (`honcho-api`) — FastAPI on :8000.
|
||||||
|
6. Deploys the Honcho deriver worker container (`honcho-deriver`) — **stopped
|
||||||
|
and disabled by default** (see below).
|
||||||
|
7. Verifies the API answers on `/docs`.
|
||||||
|
|
||||||
|
## Deriver service (disabled by default)
|
||||||
|
|
||||||
|
The `honcho-deriver` service is **created but not started** by default
|
||||||
|
(`honcho_deriver_autostart: false`). This prevents autonomous token burn.
|
||||||
|
|
||||||
|
**Why disabled:**
|
||||||
|
The deriver runs background reasoning loops that consume LLM tokens
|
||||||
|
continuously, independent of the Hermes client-side `contextCadence` and
|
||||||
|
`dialecticCadence` settings. Even with both cadences set to zero in
|
||||||
|
`~/.hermes/honcho.json`, the deriver polls Postgres every second and fires
|
||||||
|
Claude API calls autonomously.
|
||||||
|
|
||||||
|
In production, this discovered behavior cost $10 USD overnight before the
|
||||||
|
service was stopped.
|
||||||
|
|
||||||
|
**Manual start (if needed for testing):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On lincoln.local.mk-labs.cloud as jarvis or with sudo:
|
||||||
|
sudo systemctl start honcho-deriver.service
|
||||||
|
|
||||||
|
# Check status:
|
||||||
|
sudo systemctl status honcho-deriver.service
|
||||||
|
|
||||||
|
# Stop again:
|
||||||
|
sudo systemctl stop honcho-deriver.service
|
||||||
|
```
|
||||||
|
|
||||||
|
**To enable permanently:**
|
||||||
|
Override `honcho_deriver_autostart: true` in `group_vars` or inventory, then
|
||||||
|
re-run the playbook.
|
||||||
|
|
||||||
|
## Required vault entries
|
||||||
|
|
||||||
|
Add to `group_vars/all/vault` (ansible-vault encrypted):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
vault_honcho_database_password: "<strong random>"
|
||||||
|
vault_honcho_jwt_secret: "<32+ byte random>"
|
||||||
|
vault_honcho_anthropic_api_key: "sk-ant-..."
|
||||||
|
```
|
||||||
|
|
||||||
|
## LLM provider switching
|
||||||
|
|
||||||
|
This role starts with Anthropic Claude. To swap to a local
|
||||||
|
OpenAI-compatible endpoint later (e.g. astro-orbiter once we have the
|
||||||
|
GPU running llama.cpp/Ollama in OpenAI-compatible mode), override these
|
||||||
|
in `group_vars/honcho_server` or via a playbook variable:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
honcho_llm_transport: "openai"
|
||||||
|
honcho_deriver_model: "qwen3:8b"
|
||||||
|
honcho_summary_model: "qwen3:8b"
|
||||||
|
honcho_dialectic_model: "qwen3:8b"
|
||||||
|
# and add LLM_OPENAI_API_KEY (or set the base URL override in the template)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Traefik route
|
||||||
|
|
||||||
|
The Traefik file-provider YAML for `hall-of-presidents.local.mk-labs.cloud`
|
||||||
|
lives at `boilerplates/traefik/dynamic/honcho.yml`. It points at
|
||||||
|
`http://lincoln.local.mk-labs.cloud:8000`.
|
||||||
|
|
||||||
|
## Known Quadlet pitfall (carried from the semaphore role)
|
||||||
|
|
||||||
|
Quadlet regenerates the systemd unit on `.container` file change but does
|
||||||
|
NOT restart the running container. This role explicitly handles that by
|
||||||
|
gating a `state: restarted` task on the template's `changed` status —
|
||||||
|
the same pattern documented in `homelab-application-deployment`'s
|
||||||
|
quadlet-patterns reference.
|
||||||
|
|
||||||
|
## Honcho version pinning
|
||||||
|
|
||||||
|
The `honcho_image` default is `ghcr.io/plastic-labs/honcho:latest`.
|
||||||
|
Move to a digest-pinned tag once we've validated the deployment works
|
||||||
|
against a known-good build.
|
||||||
159
ansible/roles/honcho/defaults/main.yml
Normal file
159
ansible/roles/honcho/defaults/main.yml
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# honcho role defaults
|
||||||
|
# ============================================================================
|
||||||
|
# Deploys Honcho (https://honcho.dev — plastic-labs/honcho) as a rootful
|
||||||
|
# Podman + Quadlet service on a single VM. Three containers:
|
||||||
|
#
|
||||||
|
# honcho-postgres pgvector-enabled PostgreSQL backing store
|
||||||
|
# honcho-api FastAPI server on :8000 (theory-of-mind read/write)
|
||||||
|
# honcho-deriver background worker that consumes the queue and calls
|
||||||
|
# out to the configured LLM provider for derivations
|
||||||
|
#
|
||||||
|
# All three share a user-defined podman network. State persists to named
|
||||||
|
# volumes. Traefik on lightning-lane terminates TLS and routes the
|
||||||
|
# `hall-of-presidents.local.mk-labs.cloud` host to honcho-api:8000.
|
||||||
|
#
|
||||||
|
# LLM provider for the first deployment is Anthropic Claude. Switching
|
||||||
|
# providers is a single env-var change (see DERIVER_MODEL_CONFIG__TRANSPORT
|
||||||
|
# / SUMMARY_MODEL_CONFIG__TRANSPORT and the LLM_*_API_KEY variables).
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Image pinning
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Honcho does not yet publish a Docker Hub image we trust; we use the
|
||||||
|
# GitHub Container Registry build. Pin to a digest-stable tag.
|
||||||
|
honcho_image: "ghcr.io/plastic-labs/honcho:latest"
|
||||||
|
# pgvector/pgvector image follows the upstream postgres version channel.
|
||||||
|
honcho_postgres_image: "docker.io/pgvector/pgvector:pg16"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Container & network identifiers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
honcho_container_name: honcho-api
|
||||||
|
honcho_deriver_container_name: honcho-deriver
|
||||||
|
honcho_postgres_container_name: honcho-postgres
|
||||||
|
honcho_network_name: honcho-net
|
||||||
|
|
||||||
|
# Named podman volumes
|
||||||
|
honcho_postgres_volume: honcho_postgres_data
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Networking
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Honcho's FastAPI default port is 8000. Bind to 0.0.0.0 so Traefik on
|
||||||
|
# lightning-lane can reach it; auth is enabled (JWT) so the open port is
|
||||||
|
# not an open door.
|
||||||
|
honcho_listen_address: "0.0.0.0"
|
||||||
|
honcho_listen_port: 8000
|
||||||
|
|
||||||
|
# Public-facing URL used for absolute links / OIDC callbacks.
|
||||||
|
# Leave EMPTY to make Honcho host-agnostic — FastAPI emits relative URLs.
|
||||||
|
# Set to a fully-qualified URL only if a specific feature requires it.
|
||||||
|
honcho_web_url: ""
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PostgreSQL configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
honcho_db_user: honcho
|
||||||
|
honcho_db_name: honcho
|
||||||
|
# honcho_db_password sourced from vault below
|
||||||
|
|
||||||
|
# pgvector-enabled Postgres uses the same env vars as the stock image
|
||||||
|
honcho_postgres_initdb_args: "--encoding=UTF8 --locale=C"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# LLM provider configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Supported transports: openai, anthropic, gemini. We start with anthropic
|
||||||
|
# and can flip to a local OpenAI-compatible endpoint later by changing
|
||||||
|
# these knobs plus the API-key env-var name.
|
||||||
|
#
|
||||||
|
# Honcho has THREE distinct LLM subsystems, each with its own settings
|
||||||
|
# class and env-var prefix:
|
||||||
|
#
|
||||||
|
# DERIVER — generates observations from messages (background worker)
|
||||||
|
# SUMMARY — periodic session summaries
|
||||||
|
# DIALECTIC— answers theory-of-mind queries (the `peers/{peer}/chat` API)
|
||||||
|
#
|
||||||
|
# Honcho's dialectic subsystem uses PER-LEVEL config (minimal/low/medium/
|
||||||
|
# high/max) under DIALECTIC_LEVELS__<level>__MODEL_CONFIG__*. Defaults
|
||||||
|
# point at OpenAI, so any honcho.dev install without an OpenAI key will
|
||||||
|
# fail dialectic queries with "Missing API key for openai model config".
|
||||||
|
# We override all five levels to Anthropic below in the API template.
|
||||||
|
honcho_llm_transport: "anthropic"
|
||||||
|
honcho_deriver_model: "claude-sonnet-4-5"
|
||||||
|
honcho_summary_model: "claude-sonnet-4-5"
|
||||||
|
honcho_dialectic_model: "claude-sonnet-4-5"
|
||||||
|
|
||||||
|
# Deriver tuning. Honcho batches representation tasks until the per-batch
|
||||||
|
# token threshold is reached. For homelab use with one chatty operator,
|
||||||
|
# that means short bursts of conversation can sit unprocessed forever.
|
||||||
|
# DERIVER_FLUSH_ENABLED=true bypasses the batching threshold so each
|
||||||
|
# message is processed promptly.
|
||||||
|
#
|
||||||
|
# honcho_deriver_enabled controls whether the deriver Quadlet is created.
|
||||||
|
# honcho_deriver_autostart controls whether the service starts on boot.
|
||||||
|
# Set autostart=false to create the service but leave it disabled — useful
|
||||||
|
# after discovering the deriver burns tokens autonomously despite client-side
|
||||||
|
# cadence=0 settings.
|
||||||
|
honcho_deriver_enabled: true
|
||||||
|
honcho_deriver_autostart: false
|
||||||
|
honcho_deriver_workers: 1
|
||||||
|
honcho_deriver_polling_seconds: "1.0"
|
||||||
|
honcho_deriver_flush_enabled: true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Embeddings
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Anthropic does not provide an embedding API. Honcho defaults to
|
||||||
|
# OpenAI text-embedding-3-small, which fails without an OpenAI key.
|
||||||
|
# Three options going forward:
|
||||||
|
# 1. Set EMBED_MESSAGES=false — disables semantic search/vector recall
|
||||||
|
# but theory-of-mind derivation still works. Default for now.
|
||||||
|
# 2. Provide an OpenAI API key purely for embeddings (cheap; embeddings
|
||||||
|
# are ~$0.02 per million tokens). Set honcho_embed_messages=true and
|
||||||
|
# add vault_honcho_openai_api_key.
|
||||||
|
# 3. Stand up a local BGE/nomic embedding endpoint (e.g. on astro-orbiter
|
||||||
|
# once GPU is ready) and point Honcho at it via
|
||||||
|
# EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL.
|
||||||
|
honcho_embed_messages: true
|
||||||
|
|
||||||
|
# Embedding provider (transport must be openai-compatible). The default
|
||||||
|
# OpenAI endpoint requires honcho_openai_api_key. To swap to a local
|
||||||
|
# OpenAI-compatible embedder (e.g. Ollama), set honcho_embedding_base_url
|
||||||
|
# to the upstream /v1 URL and the API key can be any non-empty string.
|
||||||
|
honcho_embedding_transport: "openai"
|
||||||
|
honcho_embedding_model: "text-embedding-3-small"
|
||||||
|
honcho_embedding_base_url: ""
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Auth
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AUTH_USE_AUTH=true means clients must present a JWT signed with
|
||||||
|
# AUTH_JWT_SECRET. The secret lives in vault.
|
||||||
|
honcho_auth_enabled: true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Vault inputs (defined in group_vars/all/vault, encrypted with ansible-vault)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# vault_honcho_database_password - postgres role password
|
||||||
|
# vault_honcho_jwt_secret - 32+ byte random string for JWT signing
|
||||||
|
# vault_honcho_anthropic_api_key - Anthropic API key for Claude calls
|
||||||
|
# vault_honcho_openai_api_key - OpenAI API key, embeddings-only
|
||||||
|
honcho_db_password: "{{ vault_honcho_database_password }}"
|
||||||
|
honcho_jwt_secret: "{{ vault_honcho_jwt_secret }}"
|
||||||
|
honcho_anthropic_api_key: "{{ vault_honcho_anthropic_api_key }}"
|
||||||
|
honcho_openai_api_key: "{{ vault_honcho_openai_api_key }}"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Health check
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
honcho_health_check_retries: 30
|
||||||
|
honcho_health_check_delay: 2
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Quadlet location (rootful)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
honcho_quadlet_dir: /etc/containers/systemd
|
||||||
27
ansible/roles/honcho/meta/main.yml
Normal file
27
ansible/roles/honcho/meta/main.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
galaxy_info:
|
||||||
|
role_name: honcho
|
||||||
|
author: JARVIS
|
||||||
|
description: >-
|
||||||
|
Deploys Honcho (plastic-labs/honcho) — a memory + theory-of-mind layer
|
||||||
|
for stateful agents — with a pgvector-enabled PostgreSQL backing store,
|
||||||
|
via rootful Podman Quadlet on Ubuntu. Designed for the mk-labs
|
||||||
|
`lincoln` host. Fronted by Traefik at hall-of-presidents.local.mk-labs.cloud.
|
||||||
|
license: MIT
|
||||||
|
min_ansible_version: "2.14"
|
||||||
|
platforms:
|
||||||
|
- name: Ubuntu
|
||||||
|
versions:
|
||||||
|
- noble
|
||||||
|
- jammy
|
||||||
|
galaxy_tags:
|
||||||
|
- honcho
|
||||||
|
- memory
|
||||||
|
- llm
|
||||||
|
- podman
|
||||||
|
- quadlet
|
||||||
|
- homelab
|
||||||
|
|
||||||
|
# linux-baseline is applied separately as a day0 playbook. We don't depend
|
||||||
|
# on it here so this role stays composable.
|
||||||
|
dependencies: []
|
||||||
34
ansible/roles/honcho/tasks/api.yml
Normal file
34
ansible/roles/honcho/tasks/api.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Honcho API container (Quadlet)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Runs `fastapi run src/main.py` via the image's default CMD. The
|
||||||
|
# entrypoint.sh in the image runs the DB migration first; safe to do on
|
||||||
|
# every restart (idempotent).
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy Honcho API Quadlet
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: honcho-api.container.j2
|
||||||
|
dest: "{{ honcho_quadlet_dir }}/{{ honcho_container_name }}.container"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: honcho_api_quadlet
|
||||||
|
|
||||||
|
- name: Reload systemd to pick up Quadlet changes
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
when: honcho_api_quadlet.changed
|
||||||
|
|
||||||
|
- name: Ensure Honcho API container is started and enabled
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ honcho_container_name }}.service"
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- name: Restart Honcho API on Quadlet change
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ honcho_container_name }}.service"
|
||||||
|
state: restarted
|
||||||
|
when: honcho_api_quadlet.changed
|
||||||
36
ansible/roles/honcho/tasks/deriver.yml
Normal file
36
ansible/roles/honcho/tasks/deriver.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Honcho deriver worker container (Quadlet)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Same image as the API container, but runs the deriver script instead of
|
||||||
|
# the FastAPI server. Pulls jobs off the in-database queue and calls the
|
||||||
|
# configured LLM provider for theory-of-mind derivations.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy Honcho deriver Quadlet
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: honcho-deriver.container.j2
|
||||||
|
dest: "{{ honcho_quadlet_dir }}/{{ honcho_deriver_container_name }}.container"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: honcho_deriver_quadlet
|
||||||
|
|
||||||
|
- name: Reload systemd to pick up Quadlet changes
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
when: honcho_deriver_quadlet.changed
|
||||||
|
|
||||||
|
- name: Ensure Honcho deriver service exists but is disabled by default
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ honcho_deriver_container_name }}.service"
|
||||||
|
state: "{{ 'started' if honcho_deriver_autostart | bool else 'stopped' }}"
|
||||||
|
enabled: "{{ honcho_deriver_autostart | bool }}"
|
||||||
|
|
||||||
|
- name: Restart Honcho deriver on Quadlet change (only if autostart enabled)
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ honcho_deriver_container_name }}.service"
|
||||||
|
state: restarted
|
||||||
|
when:
|
||||||
|
- honcho_deriver_quadlet.changed
|
||||||
|
- honcho_deriver_autostart | bool
|
||||||
42
ansible/roles/honcho/tasks/main.yml
Normal file
42
ansible/roles/honcho/tasks/main.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# honcho / main entrypoint
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Order matters:
|
||||||
|
# 1. Podman + Quadlet support installed and ready
|
||||||
|
# 2. Network created (containers reference it by name)
|
||||||
|
# 3. Volumes created (postgres data)
|
||||||
|
# 4. Postgres started (Honcho API + deriver depend on it being ready)
|
||||||
|
# 5. Honcho API started
|
||||||
|
# 6. Honcho deriver worker started
|
||||||
|
# 7. Verify reachable on listen port
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Install Podman and dependencies
|
||||||
|
ansible.builtin.import_tasks: podman.yml
|
||||||
|
tags: [honcho, podman]
|
||||||
|
|
||||||
|
- name: Ensure podman network exists
|
||||||
|
ansible.builtin.import_tasks: network.yml
|
||||||
|
tags: [honcho, network]
|
||||||
|
|
||||||
|
- name: Ensure podman volumes exist
|
||||||
|
ansible.builtin.import_tasks: volumes.yml
|
||||||
|
tags: [honcho, volumes]
|
||||||
|
|
||||||
|
- name: Deploy PostgreSQL (pgvector) container
|
||||||
|
ansible.builtin.import_tasks: postgres.yml
|
||||||
|
tags: [honcho, postgres]
|
||||||
|
|
||||||
|
- name: Deploy Honcho API container
|
||||||
|
ansible.builtin.import_tasks: api.yml
|
||||||
|
tags: [honcho, api]
|
||||||
|
|
||||||
|
- name: Deploy Honcho deriver worker
|
||||||
|
ansible.builtin.import_tasks: deriver.yml
|
||||||
|
when: honcho_deriver_enabled | bool
|
||||||
|
tags: [honcho, deriver]
|
||||||
|
|
||||||
|
- name: Verify Honcho is reachable
|
||||||
|
ansible.builtin.import_tasks: verify.yml
|
||||||
|
tags: [honcho, verify]
|
||||||
17
ansible/roles/honcho/tasks/network.yml
Normal file
17
ansible/roles/honcho/tasks/network.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Podman network — single user-defined network shared by api/deriver/postgres
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Check if honcho network exists
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "podman network exists {{ honcho_network_name }}"
|
||||||
|
register: honcho_network_check
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Create honcho podman network
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "podman network create {{ honcho_network_name }}"
|
||||||
|
when: honcho_network_check.rc != 0
|
||||||
|
changed_when: true
|
||||||
24
ansible/roles/honcho/tasks/podman.yml
Normal file
24
ansible/roles/honcho/tasks/podman.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Podman + Quadlet prerequisites
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Mirrors the pattern from the semaphore role. Ubuntu 24.04's podman is new
|
||||||
|
# enough that Quadlet ships out of the box (>= 4.4).
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Ensure Podman is installed
|
||||||
|
ansible.builtin.package:
|
||||||
|
name:
|
||||||
|
- podman
|
||||||
|
- podman-compose
|
||||||
|
state: present
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Ensure Quadlet drop-in directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ honcho_quadlet_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
become: true
|
||||||
48
ansible/roles/honcho/tasks/postgres.yml
Normal file
48
ansible/roles/honcho/tasks/postgres.yml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# pgvector PostgreSQL container (Quadlet)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Honcho stores embeddings in pgvector, so we use the pgvector-enabled
|
||||||
|
# image rather than stock postgres. Same env-var surface as the upstream
|
||||||
|
# image; the pgvector extension is created by Honcho's migration script
|
||||||
|
# on first start (scripts/provision_db.py).
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy Honcho PostgreSQL Quadlet
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: honcho-postgres.container.j2
|
||||||
|
dest: "{{ honcho_quadlet_dir }}/{{ honcho_postgres_container_name }}.container"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
register: postgres_quadlet
|
||||||
|
|
||||||
|
- name: Reload systemd to pick up Quadlet changes
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
when: postgres_quadlet.changed
|
||||||
|
|
||||||
|
- name: Ensure Honcho PostgreSQL container is started and enabled
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ honcho_postgres_container_name }}.service"
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- name: Wait for PostgreSQL to accept connections
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >-
|
||||||
|
podman exec {{ honcho_postgres_container_name }}
|
||||||
|
pg_isready -U {{ honcho_db_user }} -d {{ honcho_db_name }}
|
||||||
|
register: pg_ready
|
||||||
|
until: pg_ready.rc == 0
|
||||||
|
retries: 30
|
||||||
|
delay: 2
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
# Quadlet does not auto-restart on .container changes — same pitfall as
|
||||||
|
# semaphore role. Force restart only when the template was modified.
|
||||||
|
- name: Restart Honcho PostgreSQL on Quadlet change
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ honcho_postgres_container_name }}.service"
|
||||||
|
state: restarted
|
||||||
|
when: postgres_quadlet.changed
|
||||||
27
ansible/roles/honcho/tasks/verify.yml
Normal file
27
ansible/roles/honcho/tasks/verify.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Post-deploy verification
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Polls the local Honcho API port until it responds. Fails loudly if the
|
||||||
|
# service doesn't come up — the operator should not get an "all green"
|
||||||
|
# playbook result while Honcho is silently broken.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Wait for Honcho HTTP endpoint
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://127.0.0.1:{{ honcho_listen_port }}/docs"
|
||||||
|
status_code: [200, 307]
|
||||||
|
return_content: false
|
||||||
|
register: honcho_ping
|
||||||
|
until: honcho_ping.status in [200, 307]
|
||||||
|
retries: "{{ honcho_health_check_retries }}"
|
||||||
|
delay: "{{ honcho_health_check_delay }}"
|
||||||
|
|
||||||
|
- name: Report Honcho status
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
Honcho API reachable on http://127.0.0.1:{{ honcho_listen_port }}/docs.
|
||||||
|
Traefik should now route hall-of-presidents.local.mk-labs.cloud to
|
||||||
|
this backend. Deriver enabled: {{ honcho_deriver_enabled }};
|
||||||
|
LLM transport: {{ honcho_llm_transport }};
|
||||||
|
deriver model: {{ honcho_deriver_model }}.
|
||||||
17
ansible/roles/honcho/tasks/volumes.yml
Normal file
17
ansible/roles/honcho/tasks/volumes.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Named podman volumes
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Honcho itself is stateless; only postgres needs durable storage.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Ensure named volumes exist
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "podman volume create {{ item }}"
|
||||||
|
register: volume_create
|
||||||
|
changed_when: "'already exists' not in (volume_create.stderr | default(''))"
|
||||||
|
failed_when:
|
||||||
|
- volume_create.rc != 0
|
||||||
|
- "'already exists' not in (volume_create.stderr | default(''))"
|
||||||
|
loop:
|
||||||
|
- "{{ honcho_postgres_volume }}"
|
||||||
85
ansible/roles/honcho/templates/honcho-api.container.j2
Normal file
85
ansible/roles/honcho/templates/honcho-api.container.j2
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# mk-labs Honcho API service (FastAPI on :8000)
|
||||||
|
# Managed by Ansible - honcho role
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Honcho API
|
||||||
|
After=network-online.target {{ honcho_postgres_container_name }}.service
|
||||||
|
Wants=network-online.target
|
||||||
|
Requires={{ honcho_postgres_container_name }}.service
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image={{ honcho_image }}
|
||||||
|
ContainerName={{ honcho_container_name }}
|
||||||
|
Network={{ honcho_network_name }}
|
||||||
|
PublishPort={{ honcho_listen_address }}:{{ honcho_listen_port }}:8000
|
||||||
|
|
||||||
|
# Run the entrypoint that performs DB migration before starting FastAPI.
|
||||||
|
Exec=/app/docker/entrypoint.sh
|
||||||
|
|
||||||
|
# -- Database ---------------------------------------------------------------
|
||||||
|
Environment=DB_CONNECTION_URI=postgresql+psycopg://{{ honcho_db_user }}:{{ honcho_db_password }}@{{ honcho_postgres_container_name }}:5432/{{ honcho_db_name }}
|
||||||
|
|
||||||
|
# -- Auth (USE_AUTH + JWT secret) -------------------------------------------
|
||||||
|
Environment=AUTH_USE_AUTH={{ honcho_auth_enabled | string | lower }}
|
||||||
|
Environment=AUTH_JWT_SECRET={{ honcho_jwt_secret }}
|
||||||
|
|
||||||
|
# -- LLM provider keys ------------------------------------------------------
|
||||||
|
{% if honcho_llm_transport == 'anthropic' %}
|
||||||
|
Environment=LLM_ANTHROPIC_API_KEY={{ honcho_anthropic_api_key }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# OpenAI key — used only by the embedding subsystem. Always present so
|
||||||
|
# that conclusion vectorisation and any later semantic-search path works
|
||||||
|
# regardless of which provider serves LLM completions.
|
||||||
|
{% if honcho_openai_api_key | length > 0 %}
|
||||||
|
Environment=LLM_OPENAI_API_KEY={{ honcho_openai_api_key }}
|
||||||
|
Environment=OPENAI_API_KEY={{ honcho_openai_api_key }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# -- Deriver subsystem ------------------------------------------------------
|
||||||
|
Environment=DERIVER_FLUSH_ENABLED={{ honcho_deriver_flush_enabled | string | lower }}
|
||||||
|
Environment=DERIVER_MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }}
|
||||||
|
Environment=DERIVER_MODEL_CONFIG__MODEL={{ honcho_deriver_model }}
|
||||||
|
|
||||||
|
# -- Summary subsystem ------------------------------------------------------
|
||||||
|
Environment=SUMMARY_MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }}
|
||||||
|
Environment=SUMMARY_MODEL_CONFIG__MODEL={{ honcho_summary_model }}
|
||||||
|
|
||||||
|
# -- Dialectic subsystem (per-level overrides; defaults are openai) ---------
|
||||||
|
Environment=DIALECTIC_LEVELS__minimal__MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }}
|
||||||
|
Environment=DIALECTIC_LEVELS__minimal__MODEL_CONFIG__MODEL={{ honcho_dialectic_model }}
|
||||||
|
Environment=DIALECTIC_LEVELS__low__MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }}
|
||||||
|
Environment=DIALECTIC_LEVELS__low__MODEL_CONFIG__MODEL={{ honcho_dialectic_model }}
|
||||||
|
Environment=DIALECTIC_LEVELS__medium__MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }}
|
||||||
|
Environment=DIALECTIC_LEVELS__medium__MODEL_CONFIG__MODEL={{ honcho_dialectic_model }}
|
||||||
|
Environment=DIALECTIC_LEVELS__high__MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }}
|
||||||
|
Environment=DIALECTIC_LEVELS__high__MODEL_CONFIG__MODEL={{ honcho_dialectic_model }}
|
||||||
|
Environment=DIALECTIC_LEVELS__max__MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }}
|
||||||
|
Environment=DIALECTIC_LEVELS__max__MODEL_CONFIG__MODEL={{ honcho_dialectic_model }}
|
||||||
|
|
||||||
|
# -- Embeddings (text-embedding-3-small via OpenAI by default) --------------
|
||||||
|
Environment=EMBED_MESSAGES={{ honcho_embed_messages | string | lower }}
|
||||||
|
Environment=EMBEDDING__MODEL_CONFIG__TRANSPORT={{ honcho_embedding_transport }}
|
||||||
|
Environment=EMBEDDING__MODEL_CONFIG__MODEL={{ honcho_embedding_model }}
|
||||||
|
{% if honcho_embedding_base_url | length > 0 %}
|
||||||
|
Environment=EMBEDDING__MODEL_CONFIG__BASE_URL={{ honcho_embedding_base_url }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# -- Vector store -----------------------------------------------------------
|
||||||
|
Environment=VECTOR_STORE_TYPE=pgvector
|
||||||
|
|
||||||
|
# -- Public-facing URL (empty -> relative URLs) -----------------------------
|
||||||
|
{% if honcho_web_url | length > 0 %}
|
||||||
|
Environment=HONCHO_PUBLIC_URL={{ honcho_web_url }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Timezone matches host baseline
|
||||||
|
Environment=TZ=America/Chicago
|
||||||
|
Environment=LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
TimeoutStartSec=180
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target default.target
|
||||||
70
ansible/roles/honcho/templates/honcho-deriver.container.j2
Normal file
70
ansible/roles/honcho/templates/honcho-deriver.container.j2
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# mk-labs Honcho deriver worker (background queue consumer)
|
||||||
|
# Managed by Ansible - honcho role
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Honcho deriver worker
|
||||||
|
After=network-online.target {{ honcho_postgres_container_name }}.service {{ honcho_container_name }}.service
|
||||||
|
Wants=network-online.target
|
||||||
|
Requires={{ honcho_postgres_container_name }}.service
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image={{ honcho_image }}
|
||||||
|
ContainerName={{ honcho_deriver_container_name }}
|
||||||
|
Network={{ honcho_network_name }}
|
||||||
|
|
||||||
|
# Invoke the package's __main__.py — src/deriver/deriver.py has no
|
||||||
|
# __main__ guard and exits 0 in ~3s if invoked directly, causing systemd
|
||||||
|
# to crash-loop the unit. python -m src.deriver hits __main__.py and
|
||||||
|
# starts the real queue processor.
|
||||||
|
WorkingDir=/app
|
||||||
|
Exec=/app/.venv/bin/python -m src.deriver
|
||||||
|
|
||||||
|
# -- Database ---------------------------------------------------------------
|
||||||
|
Environment=DB_CONNECTION_URI=postgresql+psycopg://{{ honcho_db_user }}:{{ honcho_db_password }}@{{ honcho_postgres_container_name }}:5432/{{ honcho_db_name }}
|
||||||
|
|
||||||
|
# -- Auth -------------------------------------------------------------------
|
||||||
|
Environment=AUTH_USE_AUTH={{ honcho_auth_enabled | string | lower }}
|
||||||
|
Environment=AUTH_JWT_SECRET={{ honcho_jwt_secret }}
|
||||||
|
|
||||||
|
# -- LLM provider keys ------------------------------------------------------
|
||||||
|
{% if honcho_llm_transport == 'anthropic' %}
|
||||||
|
Environment=LLM_ANTHROPIC_API_KEY=*** honcho_anthropic_api_key }}
|
||||||
|
{% endif %}
|
||||||
|
{% if honcho_openai_api_key | length > 0 %}
|
||||||
|
Environment=LLM_OPENAI_API_KEY=*** honcho_openai_api_key }}
|
||||||
|
Environment=OPENAI_API_KEY=*** honcho_openai_api_key }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# -- Deriver runtime --------------------------------------------------------
|
||||||
|
Environment=DERIVER_ENABLED={{ honcho_deriver_enabled | string | lower }}
|
||||||
|
Environment=DERIVER_WORKERS={{ honcho_deriver_workers }}
|
||||||
|
Environment=DERIVER_POLLING_SLEEP_INTERVAL_SECONDS={{ honcho_deriver_polling_seconds }}
|
||||||
|
Environment=DERIVER_FLUSH_ENABLED={{ honcho_deriver_flush_enabled | string | lower }}
|
||||||
|
Environment=DERIVER_MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }}
|
||||||
|
Environment=DERIVER_MODEL_CONFIG__MODEL={{ honcho_deriver_model }}
|
||||||
|
|
||||||
|
# -- Summary subsystem ------------------------------------------------------
|
||||||
|
Environment=SUMMARY_MODEL_CONFIG__TRANSPORT={{ honcho_llm_transport }}
|
||||||
|
Environment=SUMMARY_MODEL_CONFIG__MODEL={{ honcho_summary_model }}
|
||||||
|
|
||||||
|
# -- Embeddings -------------------------------------------------------------
|
||||||
|
Environment=EMBED_MESSAGES={{ honcho_embed_messages | string | lower }}
|
||||||
|
Environment=EMBEDDING__MODEL_CONFIG__TRANSPORT={{ honcho_embedding_transport }}
|
||||||
|
Environment=EMBEDDING__MODEL_CONFIG__MODEL={{ honcho_embedding_model }}
|
||||||
|
{% if honcho_embedding_base_url | length > 0 %}
|
||||||
|
Environment=EMBEDDING__MODEL_CONFIG__BASE_URL={{ honcho_embedding_base_url }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# -- Vector store -----------------------------------------------------------
|
||||||
|
Environment=VECTOR_STORE_TYPE=pgvector
|
||||||
|
|
||||||
|
# Timezone matches host baseline
|
||||||
|
Environment=TZ=America/Chicago
|
||||||
|
Environment=LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
TimeoutStartSec=180
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target default.target
|
||||||
35
ansible/roles/honcho/templates/honcho-postgres.container.j2
Normal file
35
ansible/roles/honcho/templates/honcho-postgres.container.j2
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
# mk-labs pgvector-enabled PostgreSQL backing store for Honcho
|
||||||
|
# Managed by Ansible - honcho role
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=PostgreSQL (pgvector) for Honcho
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image={{ honcho_postgres_image }}
|
||||||
|
ContainerName={{ honcho_postgres_container_name }}
|
||||||
|
Network={{ honcho_network_name }}
|
||||||
|
|
||||||
|
# Persistent data on a named volume. :Z relabels for SELinux; harmless on
|
||||||
|
# Ubuntu's ext4/apparmor stack and portable to any future host.
|
||||||
|
Volume={{ honcho_postgres_volume }}:/var/lib/postgresql/data:Z
|
||||||
|
|
||||||
|
Environment=POSTGRES_USER={{ honcho_db_user }}
|
||||||
|
Environment=POSTGRES_DB={{ honcho_db_name }}
|
||||||
|
Environment=POSTGRES_PASSWORD={{ honcho_db_password }}
|
||||||
|
Environment=POSTGRES_INITDB_ARGS={{ honcho_postgres_initdb_args }}
|
||||||
|
|
||||||
|
# Healthcheck — systemd doesn't act on this, but it's useful diagnostically
|
||||||
|
HealthCmd=pg_isready -U {{ honcho_db_user }} -d {{ honcho_db_name }}
|
||||||
|
HealthInterval=10s
|
||||||
|
HealthTimeout=5s
|
||||||
|
HealthRetries=3
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
TimeoutStartSec=120
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target default.target
|
||||||
5
ansible/roles/jarvis_user/defaults/main.yml
Normal file
5
ansible/roles/jarvis_user/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
jarvis_user: jarvis
|
||||||
|
jarvis_group: jarvis
|
||||||
|
jarvis_shell: /bin/bash
|
||||||
|
jarvis_sudo_nopasswd: true
|
||||||
30
ansible/roles/jarvis_user/tasks/main.yml
Normal file
30
ansible/roles/jarvis_user/tasks/main.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
- name: Create jarvis group
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: "{{ jarvis_group }}"
|
||||||
|
state: present
|
||||||
|
system: false
|
||||||
|
|
||||||
|
- name: Create jarvis user
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ jarvis_user }}"
|
||||||
|
group: "{{ jarvis_group }}"
|
||||||
|
shell: "{{ jarvis_shell }}"
|
||||||
|
create_home: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Deploy SSH public key
|
||||||
|
ansible.posix.authorized_key:
|
||||||
|
user: "{{ jarvis_user }}"
|
||||||
|
key: "{{ lookup('file', '~/.ssh/id_jarvis.pub') }}"
|
||||||
|
state: present
|
||||||
|
exclusive: true
|
||||||
|
|
||||||
|
- name: Grant passwordless sudo
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "/etc/sudoers.d/{{ jarvis_user }}"
|
||||||
|
content: "{{ jarvis_user }} ALL=(ALL) NOPASSWD:ALL\n"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0440'
|
||||||
|
when: jarvis_sudo_nopasswd | bool
|
||||||
64
ansible/roles/linux-baseline/README.md
Normal file
64
ansible/roles/linux-baseline/README.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# linux-baseline
|
||||||
|
|
||||||
|
Idempotent baseline for any Linux host in mk-labs.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
Applied on every host, this role guarantees a known-good operational baseline:
|
||||||
|
|
||||||
|
| Capability | Default | Description |
|
||||||
|
|----------------------|---------|---------------------------------------------------------------|
|
||||||
|
| `packages` | on | Installs curated common + OS-family packages, plus host extras |
|
||||||
|
| `timezone` | on | Sets timezone and locale |
|
||||||
|
| `time_sync` | on | Installs and configures chrony against `sundial` |
|
||||||
|
| `users` | on | Provisions baseline admin accounts (jarvis) with SSH + sudo |
|
||||||
|
| `ssh_hardening` | on | Deploys `/etc/ssh/sshd_config.d/10-mk-labs-hardening.conf` |
|
||||||
|
| `unattended_upgrades`| on | Security-only automatic upgrades on Debian-family hosts |
|
||||||
|
| `sysctl` | on | Drop-in at `/etc/sysctl.d/99-mk-labs.conf` |
|
||||||
|
| `journald` | on | Caps journal size + retention |
|
||||||
|
| `motd` | on | Branded MOTD |
|
||||||
|
| `full_upgrade` | off | Opt-in full dist-upgrade. Use the day2 update playbook instead |
|
||||||
|
|
||||||
|
Every capability sits behind a feature flag in `baseline_features` so it
|
||||||
|
can be disabled per-host without forking the role.
|
||||||
|
|
||||||
|
## OS support
|
||||||
|
|
||||||
|
- **First-class:** Ubuntu (22.04, 24.04) and Debian (bullseye, bookworm).
|
||||||
|
- **Placeholder:** RHEL family — see `vars/RedHat.yml`. Flesh out when a
|
||||||
|
RHEL-family host joins the fleet.
|
||||||
|
|
||||||
|
OS-specific values are loaded from `vars/{{ ansible_os_family }}.yml`.
|
||||||
|
|
||||||
|
## Required inventory variables
|
||||||
|
|
||||||
|
| Variable | Where | Example |
|
||||||
|
|-------------------------|-----------------|-------------------------------------------|
|
||||||
|
| `jarvis_ssh_public_key` | group_vars/all | `ssh-ed25519 AAAA... jarvis@mk-labs` |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Apply linux-baseline
|
||||||
|
hosts: "{{ target | default('all') }}"
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- linux-baseline
|
||||||
|
```
|
||||||
|
|
||||||
|
## Per-host overrides
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# host_vars/some-host/vars
|
||||||
|
baseline_packages_extra:
|
||||||
|
- postgresql-client
|
||||||
|
- redis-tools
|
||||||
|
|
||||||
|
baseline_features:
|
||||||
|
motd: false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migration notes
|
||||||
|
|
||||||
|
This role supersedes the legacy `day0-baseline` and `common` roles, both
|
||||||
|
of which are slated for removal once the fleet has been re-baselined.
|
||||||
120
ansible/roles/linux-baseline/defaults/main.yml
Normal file
120
ansible/roles/linux-baseline/defaults/main.yml
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# linux-baseline role defaults
|
||||||
|
# ============================================================================
|
||||||
|
# This role applies an idempotent baseline to any Linux host in mk-labs.
|
||||||
|
# Ubuntu is the first-class target; other distributions plug in via
|
||||||
|
# vars/<ansible_os_family>.yml.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Feature flags - each capability can be toggled independently
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
baseline_features:
|
||||||
|
packages: true
|
||||||
|
timezone: true
|
||||||
|
time_sync: true
|
||||||
|
ssh_hardening: true
|
||||||
|
users: true
|
||||||
|
unattended_upgrades: true
|
||||||
|
sysctl: true
|
||||||
|
journald: true
|
||||||
|
motd: true
|
||||||
|
# Off by default: a full apt/dnf upgrade is intrusive. Use the dedicated
|
||||||
|
# day2 update playbook for that. Security patches are handled by
|
||||||
|
# unattended-upgrades.
|
||||||
|
full_upgrade: false
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Locale & time
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
baseline_timezone: America/Chicago
|
||||||
|
baseline_locale: en_US.UTF-8
|
||||||
|
baseline_ntp_server: sundial.local.mk-labs.cloud
|
||||||
|
baseline_ntp_fallback_pool: 2.pool.ntp.org
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Packages
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Distribution-agnostic baseline. Distro-specific additions are merged in
|
||||||
|
# from vars/<family>.yml at runtime.
|
||||||
|
baseline_packages_common:
|
||||||
|
- curl
|
||||||
|
- wget
|
||||||
|
- vim
|
||||||
|
- htop
|
||||||
|
- git
|
||||||
|
- jq
|
||||||
|
- unzip
|
||||||
|
- rsync
|
||||||
|
- ca-certificates
|
||||||
|
- gnupg
|
||||||
|
- net-tools
|
||||||
|
- dnsutils
|
||||||
|
- acl
|
||||||
|
- tree
|
||||||
|
- tmux
|
||||||
|
|
||||||
|
# Per-host opt-in extras. Set in host_vars or group_vars.
|
||||||
|
baseline_packages_extra: []
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Baseline users
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 'jarvis' is the universal admin / automation account that this role
|
||||||
|
# provisions on every host. Service users (e.g. 'cast' for podman
|
||||||
|
# workloads) are created by their respective application roles, not here.
|
||||||
|
baseline_users:
|
||||||
|
- name: jarvis
|
||||||
|
comment: "JARVIS automation account"
|
||||||
|
shell: /bin/bash
|
||||||
|
groups: "{{ baseline_sudo_group }}"
|
||||||
|
sudo_nopasswd: true
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- "{{ jarvis_ssh_public_key }}"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# SSH hardening
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Delivered as a drop-in under /etc/ssh/sshd_config.d/ rather than editing
|
||||||
|
# the main config file. Safer to re-apply, easier to remove.
|
||||||
|
baseline_ssh_permit_root_login: "no"
|
||||||
|
baseline_ssh_password_authentication: "no"
|
||||||
|
baseline_ssh_pubkey_authentication: "yes"
|
||||||
|
baseline_ssh_permit_empty_passwords: "no"
|
||||||
|
baseline_ssh_x11_forwarding: "no"
|
||||||
|
baseline_ssh_client_alive_interval: 300
|
||||||
|
baseline_ssh_client_alive_count_max: 2
|
||||||
|
baseline_ssh_max_auth_tries: 4
|
||||||
|
baseline_ssh_login_grace_time: 30
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Unattended security upgrades (apt / dnf-automatic)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
baseline_unattended_security_only: true
|
||||||
|
baseline_unattended_auto_reboot: false
|
||||||
|
baseline_unattended_auto_reboot_time: "03:00"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# sysctl
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Light hand here. The kernel defaults are generally sensible; we tune
|
||||||
|
# only what consistently bites us in a homelab.
|
||||||
|
baseline_sysctl:
|
||||||
|
vm.swappiness: 10
|
||||||
|
fs.inotify.max_user_watches: 524288
|
||||||
|
fs.inotify.max_user_instances: 512
|
||||||
|
net.ipv4.tcp_keepalive_time: 600
|
||||||
|
net.ipv4.tcp_keepalive_intvl: 60
|
||||||
|
net.ipv4.tcp_keepalive_probes: 6
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# journald retention caps
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
baseline_journald_max_use: "1G"
|
||||||
|
baseline_journald_max_retention: "30day"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# MOTD branding
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
baseline_motd_banner: "mk-labs"
|
||||||
20
ansible/roles/linux-baseline/handlers/main.yml
Normal file
20
ansible/roles/linux-baseline/handlers/main.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
- name: Restart chrony
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ baseline_chrony_service }}"
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: Restart sshd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ baseline_sshd_service }}"
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: Reload sysctl
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: sysctl --system
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Restart systemd-journald
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: systemd-journald
|
||||||
|
state: restarted
|
||||||
25
ansible/roles/linux-baseline/meta/main.yml
Normal file
25
ansible/roles/linux-baseline/meta/main.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
galaxy_info:
|
||||||
|
role_name: linux_baseline
|
||||||
|
author: JARVIS
|
||||||
|
description: >-
|
||||||
|
Idempotent Linux baseline for mk-labs hosts. Ubuntu first-class,
|
||||||
|
pluggable for other distributions via vars/<os_family>.yml.
|
||||||
|
license: MIT
|
||||||
|
min_ansible_version: "2.14"
|
||||||
|
platforms:
|
||||||
|
- name: Ubuntu
|
||||||
|
versions:
|
||||||
|
- jammy
|
||||||
|
- noble
|
||||||
|
- name: Debian
|
||||||
|
versions:
|
||||||
|
- bullseye
|
||||||
|
- bookworm
|
||||||
|
galaxy_tags:
|
||||||
|
- baseline
|
||||||
|
- bootstrap
|
||||||
|
- hardening
|
||||||
|
- homelab
|
||||||
|
|
||||||
|
dependencies: []
|
||||||
21
ansible/roles/linux-baseline/tasks/journald.yml
Normal file
21
ansible/roles/linux-baseline/tasks/journald.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# journald retention
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Ensure journald drop-in directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/systemd/journald.conf.d
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Deploy journald retention drop-in
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: journald-retention.conf.j2
|
||||||
|
dest: /etc/systemd/journald.conf.d/10-mk-labs-retention.conf
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
notify: Restart systemd-journald
|
||||||
63
ansible/roles/linux-baseline/tasks/main.yml
Normal file
63
ansible/roles/linux-baseline/tasks/main.yml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# linux-baseline / main entrypoint
|
||||||
|
# ============================================================================
|
||||||
|
# Orchestrates baseline application. Each capability sits behind a
|
||||||
|
# feature flag in baseline_features so individual concerns can be
|
||||||
|
# disabled per-host without forking the role.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Preflight checks
|
||||||
|
ansible.builtin.import_tasks: preflight.yml
|
||||||
|
tags: [baseline, preflight, always]
|
||||||
|
|
||||||
|
- name: Load OS-family-specific variables
|
||||||
|
ansible.builtin.include_vars: "{{ ansible_os_family }}.yml"
|
||||||
|
tags: [baseline, always]
|
||||||
|
|
||||||
|
- name: Apply package baseline
|
||||||
|
ansible.builtin.import_tasks: packages.yml
|
||||||
|
when: baseline_features.packages | bool
|
||||||
|
tags: [baseline, packages]
|
||||||
|
|
||||||
|
- name: Configure timezone and locale
|
||||||
|
ansible.builtin.import_tasks: timezone.yml
|
||||||
|
when: baseline_features.timezone | bool
|
||||||
|
tags: [baseline, timezone]
|
||||||
|
|
||||||
|
- name: Configure time synchronisation
|
||||||
|
ansible.builtin.import_tasks: time_sync.yml
|
||||||
|
when: baseline_features.time_sync | bool
|
||||||
|
tags: [baseline, time-sync]
|
||||||
|
|
||||||
|
- name: Provision baseline users
|
||||||
|
ansible.builtin.import_tasks: users.yml
|
||||||
|
when: baseline_features.users | bool
|
||||||
|
tags: [baseline, users]
|
||||||
|
|
||||||
|
- name: Harden SSH
|
||||||
|
ansible.builtin.import_tasks: ssh_hardening.yml
|
||||||
|
when: baseline_features.ssh_hardening | bool
|
||||||
|
tags: [baseline, ssh, hardening]
|
||||||
|
|
||||||
|
- name: Configure unattended security upgrades
|
||||||
|
ansible.builtin.import_tasks: unattended_upgrades.yml
|
||||||
|
when:
|
||||||
|
- baseline_features.unattended_upgrades | bool
|
||||||
|
- ansible_os_family == "Debian"
|
||||||
|
tags: [baseline, unattended-upgrades]
|
||||||
|
|
||||||
|
- name: Apply sysctl baseline
|
||||||
|
ansible.builtin.import_tasks: sysctl.yml
|
||||||
|
when: baseline_features.sysctl | bool
|
||||||
|
tags: [baseline, sysctl]
|
||||||
|
|
||||||
|
- name: Configure journald retention
|
||||||
|
ansible.builtin.import_tasks: journald.yml
|
||||||
|
when: baseline_features.journald | bool
|
||||||
|
tags: [baseline, journald]
|
||||||
|
|
||||||
|
- name: Apply MOTD branding
|
||||||
|
ansible.builtin.import_tasks: motd.yml
|
||||||
|
when: baseline_features.motd | bool
|
||||||
|
tags: [baseline, motd]
|
||||||
12
ansible/roles/linux-baseline/tasks/motd.yml
Normal file
12
ansible/roles/linux-baseline/tasks/motd.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# MOTD branding
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy mk-labs MOTD
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: motd.j2
|
||||||
|
dest: /etc/motd
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
32
ansible/roles/linux-baseline/tasks/packages.yml
Normal file
32
ansible/roles/linux-baseline/tasks/packages.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Package baseline
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Update apt cache (Debian family)
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: true
|
||||||
|
cache_valid_time: 3600
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Install baseline packages (common)
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "{{ baseline_packages_common }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Install baseline packages (OS family)
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "{{ baseline_packages_family }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Install baseline packages (host extras)
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "{{ baseline_packages_extra }}"
|
||||||
|
state: present
|
||||||
|
when: baseline_packages_extra | length > 0
|
||||||
|
|
||||||
|
- name: Perform full system upgrade (opt-in)
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "*"
|
||||||
|
state: latest # noqa package-latest - intentional under feature flag
|
||||||
|
when: baseline_features.full_upgrade | bool
|
||||||
18
ansible/roles/linux-baseline/tasks/preflight.yml
Normal file
18
ansible/roles/linux-baseline/tasks/preflight.yml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Preflight: fail fast on unsupported platforms.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Assert a supported OS family
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ansible_os_family in ["Debian", "RedHat"]
|
||||||
|
fail_msg: >-
|
||||||
|
linux-baseline currently supports the Debian and RedHat OS families.
|
||||||
|
Host {{ inventory_hostname }} is running {{ ansible_distribution }}
|
||||||
|
{{ ansible_distribution_version }} ({{ ansible_os_family }}).
|
||||||
|
success_msg: "Detected {{ ansible_distribution }} {{ ansible_distribution_version }}."
|
||||||
|
|
||||||
|
- name: Gather package facts
|
||||||
|
ansible.builtin.package_facts:
|
||||||
|
manager: auto
|
||||||
43
ansible/roles/linux-baseline/tasks/ssh_hardening.yml
Normal file
43
ansible/roles/linux-baseline/tasks/ssh_hardening.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# SSH hardening
|
||||||
|
# ============================================================================
|
||||||
|
# Delivered as a drop-in under /etc/ssh/sshd_config.d/. The main
|
||||||
|
# sshd_config remains untouched so distro upgrades don't conflict and
|
||||||
|
# rollback is trivial (delete the drop-in).
|
||||||
|
#
|
||||||
|
# Some older sshd builds (Ubuntu < 20.04, Debian < 11) do not honour
|
||||||
|
# sshd_config.d/. We fall back to lineinfile on those.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Check whether sshd supports the Include directive
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: sshd -T
|
||||||
|
register: sshd_test
|
||||||
|
changed_when: false
|
||||||
|
check_mode: false
|
||||||
|
|
||||||
|
- name: Determine if /etc/ssh/sshd_config.d/ is honoured
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /etc/ssh/sshd_config.d
|
||||||
|
register: sshd_dropin_dir
|
||||||
|
|
||||||
|
- name: Ensure /etc/ssh/sshd_config.d exists when honoured
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/ssh/sshd_config.d
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
when: sshd_dropin_dir.stat.exists
|
||||||
|
|
||||||
|
- name: Deploy SSH hardening drop-in
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: sshd_hardening.conf.j2
|
||||||
|
dest: /etc/ssh/sshd_config.d/10-mk-labs-hardening.conf
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
validate: "sshd -t -f %s"
|
||||||
|
when: sshd_dropin_dir.stat.exists
|
||||||
|
notify: Restart sshd
|
||||||
16
ansible/roles/linux-baseline/tasks/sysctl.yml
Normal file
16
ansible/roles/linux-baseline/tasks/sysctl.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# sysctl baseline
|
||||||
|
# ============================================================================
|
||||||
|
# Delivered as a drop-in under /etc/sysctl.d/ so each setting is
|
||||||
|
# self-documenting and removable.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy mk-labs sysctl drop-in
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: 99-mk-labs.conf.j2
|
||||||
|
dest: /etc/sysctl.d/99-mk-labs.conf
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
notify: Reload sysctl
|
||||||
25
ansible/roles/linux-baseline/tasks/time_sync.yml
Normal file
25
ansible/roles/linux-baseline/tasks/time_sync.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Time synchronisation via chrony
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Install chrony
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: chrony
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Deploy chrony configuration
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: chrony.conf.j2
|
||||||
|
dest: "{{ baseline_chrony_config_path }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
backup: true
|
||||||
|
notify: Restart chrony
|
||||||
|
|
||||||
|
- name: Ensure chrony is enabled and running
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "{{ baseline_chrony_service }}"
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
24
ansible/roles/linux-baseline/tasks/timezone.yml
Normal file
24
ansible/roles/linux-baseline/tasks/timezone.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Timezone and locale
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Set system timezone
|
||||||
|
community.general.timezone:
|
||||||
|
name: "{{ baseline_timezone }}"
|
||||||
|
|
||||||
|
- name: Ensure locale package present (Debian)
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: locales
|
||||||
|
state: present
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Generate locale
|
||||||
|
community.general.locale_gen:
|
||||||
|
name: "{{ baseline_locale }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Set system locale
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "localectl set-locale LANG={{ baseline_locale }}"
|
||||||
|
changed_when: false
|
||||||
37
ansible/roles/linux-baseline/tasks/unattended_upgrades.yml
Normal file
37
ansible/roles/linux-baseline/tasks/unattended_upgrades.yml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# Unattended security upgrades (Debian family)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Ensure unattended-upgrades is installed
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- unattended-upgrades
|
||||||
|
- apt-listchanges
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Deploy 50unattended-upgrades configuration
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: 50unattended-upgrades.j2
|
||||||
|
dest: "{{ baseline_unattended_config_dir }}/50unattended-upgrades"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
backup: true
|
||||||
|
|
||||||
|
- name: Enable periodic upgrade timers
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "{{ baseline_unattended_config_dir }}/20auto-upgrades"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
APT::Periodic::Update-Package-Lists "1";
|
||||||
|
APT::Periodic::Unattended-Upgrade "1";
|
||||||
|
APT::Periodic::AutocleanInterval "7";
|
||||||
|
|
||||||
|
- name: Ensure unattended-upgrades timer is running
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: unattended-upgrades
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user