9 Commits
Author SHA1 Message Date
JARVISandClaude Opus 5.5 78ac06378e multica_agent_host: op shim reads the service account token (WDWL-37)
Deliver vault_op_service_account_claude to the agent hosts and install a
role-owned ~multica/.local/bin/op shim that exports OP_SERVICE_ACCOUNT_TOKEN
to /usr/bin/op only. op run / op plugin run get env -u spliced after "--"
so the child never sees the token. The daemon unit is untouched (WDWL-18).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:36:23 -05:00
rblundon 0d674e0c4b update vault 2026-10-23 17:34:02 -05:00
rblundon de9e340151 added 1password token to vault 2026-10-23 17:15:03 -05:00
JARVISandClaude Opus 5.5 be7d4a4378 multica_agent_host: install the 1Password CLI from 1Password's signed repo (WDWL-37)
Agents get `op` so op:// references resolve at command time and secret
values stay out of prompts and transcripts. Installed per 1Password's
apt instructions (signed-by keyring, debsig policy + keyring,
1password-cli), with the key fingerprint-checked before it is trusted;
EL via their yum repo. multica_op_version pins, "" tracks the repo.
Tag: multica_op.

Optional, default-off hook writes OP_SERVICE_ACCOUNT_TOKEN from the
playbook (ansible-vault) to a 0600 file owned by the daemon user. It is
deliberately not wired into the daemon environment: the user unit
belongs to the multica CLI, and the delivery design is pending.

Rolled out with --tags multica_op to haunted-mansion and spaceship-earth;
second run changed=0, op 2.41.1 as multica, debsig verifies the .deb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:11:27 -05:00
JARVISandClaude Opus 5.5 01e6ad90f0 traefik: serve gitea on mad-tea-party.wdw-labs.cloud too, add SAN (WDWL-27)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:51:28 -05:00
JARVISandClaude Opus 5.5 008fd5bf7b gitea: template compose from role vars; bump to 28.2.0 / postgres 17.11 (WDWL-27)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:51:28 -05:00
JARVISandClaude Opus 5.5 fc2ad85e19 obsidian_headless: grant the service user on shared folders too (WDWL-22)
Content multica creates in inbox/ is owned multica:multica, so obsync fell
into "other" (---) and Sync could neither read nor upload Wong's
submissions. Found after the real Sync sign-in on spaceship-earth; the
earlier verify only tested the obsync -> multica direction.

Shared folders now carry user:obsync:rwx as an access and a default entry
alongside the shared user's, and verify tests the multica -> obsync
direction (read + rewrite, as merge needs). Second run changed=0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:28:52 -05:00
JARVISandClaude Opus 5.5 2d039ae32f multica_agent_host: merge declared keys into the daemon user's Claude settings (WDWL-22)
The npm-installed Claude Code on spaceship-earth updated itself from the
pinned 2.1.304 to 2.1.308 right after its first login, so every play would
downgrade it and it would update again. New multica_claude_settings is
merged (recursively, not templated, since Claude writes the file too) into
~multica/.claude/settings.json.

provision-agent-host.yml sets DISABLE_AUTOUPDATER=1 and mirrors the model
mix already live on haunted-mansion: model sonnet, subagents haiku,
MAX_THINKING_TOKENS 4000, BASH_MAX_OUTPUT_LENGTH 20000, thinking off.

spaceship-earth: back on 2.1.304 and staying there; second run changed=0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:38:00 -05:00
JARVISandClaude Opus 5.5 94c77daf13 multica_agent_host: daemon lifecycle via systemd; fix login guard (WDWL-22)
Found provisioning spaceship-earth:

- Login was not idempotent: `multica auth status` prints to stderr (0.6.5),
  and the guard read stdout only, so every run re-logged in and restarted
  the daemon. The guard now reads both streams; the status read is no_log
  because it echoes a token prefix.
- `multica daemon start` / `daemon restart` fork the daemon inside the
  caller's session (Ansible's SSH session scope), outside the systemd user
  unit, which stays inactive: the same orphan seen on haunted-mansion on
  2026-10-21. Start/restart now go through `systemctl --user`, after a
  daemon-reload when autostart was (re)registered. A daemon found running
  outside its unit is stopped first. The unit name is read from
  `autostart status`, so --profile hosts resolve their own unit.

spaceship-earth: second run changed=0, daemon in multica-daemon.service.
Not yet run against haunted-mansion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:04:46 -05:00
20 changed files with 1225 additions and 502 deletions

No files matched your search

+471 -426
View File
@@ -1,427 +1,472 @@
$ANSIBLE_VAULT;1.1;AES256
31356235343830393937633039386366623238373864353438353332653038616330623365343135
3961393463613335663138383233323964393832626466620a393763616364363030323833386565
30666333366332313262313836613962363537353065646361316230386434366534666338336636
3930363231393333610a613039646430663233613163376131333637383866656564333765633565
32333338316539363034316132313631643632316337356164373765373736366639623362343738
63326164623535326461323234333933656132313836643139316339333831323331393030323233
34643932626464353861616534326130373362646437613131326464313737623965366361616266
37393934333761303163346465366464393964313831393266303434313061356333373430346339
33393331393461303163306639386239656139613833386235363335333837316666323637346462
35346634383936333631303338356433323865623030663336323663373962623531313562626666
33393063613137646631666363386161636462343262313935386535386361346464396133613736
31383464613130353034396264643766646161343664363435383639656234623035343833653832
63356130646136396463636533616334643638333239636437663030393064326362393736373233
34396166653932326632376535373762663633303261303536396334303937316431346133353830
37626337313861306164366130643131383133323337643637666535316132373238373066306339
38343865613937663366376630333365633039333430653762313864303737633661356232373133
64303934623937313863323431396638383932383836613830336235656530613234316238353364
32306561343362643431623239333638316239323164386634623736313261313936616239396431
64373934313663313139316331623436663832393131373562393436326265656562363137636461
33613864356464366239306639653264363565306331636137396466336163336631353163383430
30303034383136356465643238663034623036363562353037343832616136363539656432346639
34636536383533333538346237653731633331643137303966316138333633373564383034383438
34666465323032356333613935363161306431666533353061626536633839396234613662623062
63313338346138653765393537356639373035643935373735323166376336626166396562316535
39343236356665363038326132313437303236646161333638393939653033623664306563613730
34383538656139646436333765623861303739613939633962316139386463643666366464303935
32366239656366393832376436623534653539376661363737343865663633316138663061643866
65393930386162393663303135396237623633656230303036646431633236376465633835336364
63643332616433303766396561386433363836343836663430636530616135653966336131383533
64613933306165303564303835616132613839636432653137356131343732343736313237356436
35663033353163346363366635333231366432316434306362616265653832313963303338343538
61363966306162323932393037626335613133363936393166626433653463363661343732306465
32373839653865326333613165353461613531383666323938653034623936303562363331643363
37326164323935626239396239646465616464613332636662353539363233343032616636613237
62316663643134663038626535663262336266646565336236643234663561313264626238313136
65383364643537393436666465653437653334663934333765626233303835643732373334663639
62643064383535366630306666383365353963306563323533653664643132383832346435323465
30326466623265326437383063353833383131393166313231633032333137326563633734636263
61623466613436393237656435623335623133343032333535373430633836346566353039646238
34363338343065323765313466306130643462303061336265623833343139353336383234366362
30383465376635356462313533643961613437333930396134626463383464323661396431643234
30353737396262653336376137646137353362353066366634613034313938613234323931333365
39323733613864346132633461323563396161643738333634316564353934376537353663386636
36373335343439643432396266386665396266643063616331666438363066643162356265373732
31616537656266363866656133316663383634343735616266653161613139636164653566303666
35396566653136343038636239626561303966346636333635363836366538616233386164316633
30626135303434636335623366383566366532313937313133313664313462316438323964376130
61363166306232363737616432316666623166313962366462656464626537323538316337626635
64366533383264356430343066373664646534396538306335333861356237346164373235643239
31633264396338303061376633376564356231336338376135386639393031656437363662613861
33373934313862396238353863663530633337383633623565623937623734616664626534383061
34353862393537633932393338653265353633643166383564363766636539636334643534333736
62666332313730666661303462656162303338326433393934313536326365663235346263313330
34623539383031653538333762396361336235643936386366323235346162326635393062646263
36343638613134353966363261613632313963363236326364366133353838616636323138316561
63356237663864333334343461396364656231333939636264343966343266346430363965633764
38636562626366636438343538666632643964393463353836323536333961613961613166373963
62356336396536613238353531616237616334616231363836616561333866643033666336336439
65646362393933306534326137396561346338336263383134383263373836323034653932646463
62326364336438623137623862386438396164643432333135346462363761333437653532373361
32306161313238363365633131663334376135613336363937313263663537313733313762656134
33613838326137343061306431363237373661333163373739326339303732623131393633646565
38663963663338623362653364343337383636323730306431313830316261666264326464303235
64313662326434646366386166396464653666323735323931343436653763363535396636613938
37386435303139663965623963383861353030626530653061636132326233373165613231316232
38616539303630316565366161383664316337333665323631663430363764313736643934623638
35346264666461623533623963306238613530633034333662653561353030646437346537623065
37376263366630666430383833373966353730396239626133333330306232643037643231336433
62653332623461653565376531386464353237383665323538363061633831353534656366333933
37373235376564613665303462393634623836663835383738356138323136316432663637353434
36336338623364383334363565643334613631386330633466396336336539383762383162363631
32393230623938623433333632663365313165306165643433643131366237366131616263333465
38333739636234326139393361386131383232373362343636653862356535613230343232333365
30316261623036373531383238373164313961386532393663643736363131623533373366613565
37653133383930663037363232353032616133653763306564383434366233613066373331363162
61366530303731333235313537366436363237303561336232663363396562313735613461363331
30336561643035336430643666643565653538616536646263303265663739653132353233343834
36613332313430313138636464393139316466373738313330383762623963376363663839386366
36636265383436643661653465383561306663303461633335373331323733663936373334353164
35353138306233626363616636633236643130373432636161646563333937623334636163653637
31613431363963396266303637363833303635376239356337613935636165326265303166383062
32326165636331356466363766643766363266666530363739383535623863393135663331623737
61303261313563643665626435343362656632363263656235316230646135616163346630363335
36633564383761346163383166326562313464316362613531633565646365326538356565386630
35396334353834343733643931376261336435373362386339353138386435663961623064343438
34633639383037653437653461346563373733663536313862386262393933623434313163313136
64333531656462353966353362646636353634646433333361633166653338373938326639343732
30313163613532633163663965393434393735383730653430663762336262393564303263643935
32313564393835353762363662373631643430393938643265393462643864633465643636306664
32363936303737363934373536383136613636323538363565306563653438343961373566363463
34323338636161316537383036643638366430303964376435356164333666313833303264666464
64383766356630313130626235663364653730666638363336353938323735356434336539383132
61633334383661613039373136613735306662633734346538663838653132343066313166333834
33386533306466613037383636636432363435616266373664636631623264396538626366616164
63363831616562653034666231386365666639343330303964313165616462623865663266326531
38306337376133613932656631393532613431323964643532386234303335643731373265643536
39336233363530313533663163616462333466656364666262313132636633613863343766656465
38393339386335623234646437393061366230643432636163306432633365356330306234396530
37346466333961383538356236303630353131376330613437656536393766383062323235643465
38663034393633383661363632386431343364333664396530323066306365366539366336333265
34343865633834333564343237336661613437363333373032393661633034643161623461306362
33613663613966636235333664313731333064613033613639656232626365366461373535376532
37613365393237333037653730323432386162616165663335653565666565656438373337376232
35663933646636336535653431613337313562386137363137616561346435636562656337653265
62396430663033373234366535633838306335306263616561323438343736326664653436663432
61396237383030316436306130663039623963343362326465333933373061643165353465383761
66336433356538313761616663363438316165653763306431353139656361346631336538343936
37643363363662626362643333613431363032323938396365373337643235353333393232373862
38316664383838623836343938663231623237306566353036633265316361636565366134646562
61383161313664633736613463393035363637633836636634363231326230613437663438343362
64313362653131316433303561366536373436383865333138376663353035343731363934633537
37663832316265356535643561623435336563663362663766306662326564383865373565333134
37343035363763373236656239376665663536366131306439643331663731656234386432653665
30393632653939393564363634373334623865626231653639356633653136363630306539356361
34396238393339616333393734633835626463353932616262383263336439366262303966333666
39666536363537633030393730343037623265643438313166333064303635663430323435383438
31313364336662363365303262653566303830633930666639656234373037366238643431626562
31373137663434366564373833656233636165636636653738383035393936343063303761353265
66393938643238393338343930363233326463643237633137393566316438393266613731346162
39393630316164613139393436316533333632366638663437366266323837633937613539643434
38316132306333633666633433623565323463636664336536616266306262653064363931316165
34383233343964326363363235343165653339653162326161323932373631353665353164653731
34333665313065306439393431336632356536646334376562646232353938313836313735616238
63623765313564353463653133333536343937663165396463303531616365616363383938313939
37373530363830386338393661306261616432363737303236613232616232643666653464613937
35313631616166386330653730653062646234333432336135316235356166343139316538353337
38663265633166383563623561656632386364663365326662323036316436386639663761323861
33313937343632626561663233323862636335373930366233326163623831363765643236666463
65386234646363313039646266636435366462383266396337383439623437306166663536363966
34333762366161373531633036356139646338363031366561363863393830353336343337363664
32366263343464663338306562636664333035616165363538333764616139663063326232326664
63623264303965353632303230653636313162373061396436643064353632323731633435623232
63313263616339613065313138613032626635646236353462306263376638643966313332393939
36643637653261663830313564316364643832383330353165613666333733646138633432633763
61633431396432313863343466306266666133646266336536646635666631613633346430633539
63313964353937386636626532303532353133366237383566393061326265316163613665326131
38613232366132313762393436646265326361323736643338623035323336326539616432386630
65346333643936343835376364626539303265313239376338653835343232646362656366646264
61626463363665363737323331356361616364626565333561303836313966323461663438333933
65643533346362626137363762363663383761353364363164393062383830383931653262383564
35333532646661623731663938656339323739663964666238653963323232666664313237363137
38626438313265336263626365643763393532613831343763393130343766663335343431613038
31393938303162346236303635306239613335656338643362376539346335373130323935393133
62356533393363626139323632663865373837306337393433353963396231643937373762653837
38306331656262343832333136333536383035323532383766383538653831386531353936363936
66623762663266663135373165303233646262653465633930373832383364386263383831366461
32643533386438613533326566336538633632313331396535326562336263646334336362346634
30366562623663313638356233633433306564303065316337323862663533366366353230373434
64656437626135633932616230333339373934613666383565626535663939313930373230313130
39333434643834356634383133383533396264663831383736326162323431393464613736616539
38326533623734353937373135386664613635636264383130346263326462303531316232386131
30663833633738363533333662376461313331636230346538313764633135303331613039316363
34623937386336646339373366663563626430343866303239363661313634636333616232336438
66616132363337393334646333623737336633303331393030326561613835613164303965393932
65313665303932373331656434353634343561353236643932623464393566333037386334363066
39373163313838663163323666636337663161343664646661616438393333363365363065353732
61666666666563626432383639393038633233663561303561353238613832373633653436316366
65333030623565336266646563333336373339616231366334316336666463633964323630373634
34346131626466316163366362326265613363333933336463623961306637353639346537316336
63383561353339393963396339663738333765663934383038313631363733356562373935333765
38663735636562336564636235306135666530303762623331663931366163363734316539333734
33303638643166303165653062396231613862313165373564626432313961643835383038316437
35653639656362633631336635363035633635623035653439353561343739626637656566333065
61626536366535376237366664646534383534323331633035326632656132646364363133636133
65643065383337356136633534383033396563353034383462386230623261636337303537646463
38356538356434393864303563336563343566306638353138656561663930316230353433386362
61643561383739366362666633616365343235393363623035316263313738633662323961613931
35393234373730616133316462363661663866323466333839656462636133613936643137653833
62333030343861356365616339363134353463656162616636303430356136363638313861313637
37613731663639633865353332653835316631663364646139623631613138633162366432333532
30306432653133333637623631626165633537623265313237323532303566383334633337306564
31643163633934633161316663336334316237353735313063376436393166313033313665366265
35363864386265386139353735663238366663396366653164613833383336653230383763316134
37386633376538366633343963303339323338303263343862396631613733336261356536396264
37623236383565323331306531316563356238316237636631613736316437643965613634666437
32366661346466353261336130363233613036633863303161303931346462633763353038363136
36363762353231373933616265643039666237333334326134396531643463336238303862313465
62346431336663633661303034376165643539323061653363613635343761326363616139313933
64613430353539376131653439346337353733663830306333373966393162366235393364336532
38613936303965646231323138643464636133393037363538373562396230363434386230363934
30656361323137326335316536663231376536646565643363323462363165633139346535313733
61666434643537363135613862656462653732306665623037363638613832323530653836643137
37323233316131343039643137333532653165316137303032373635346539656635363265396565
34663263396265383532306264376430313237616438383639313863663664393066643565623636
30376136373833653462646561396238356638336432376630306465333133623538636333623961
33316263373862643166623030316239383433313365313439386534336532653436633034643938
34613737333933303934653635636239363332376436303063643566663536353266303363626539
64393737383037383336373663383163303538343438306361656164356563653236383563633735
61376362303866646130666234616362323830643238353433613831353635373636306363623262
65393266313430653133316639326330383936303566613534316366613637623465643739323234
36646138353135666265646137653138656262303864653839333839633336643466343066653865
31323538353566663638316136653438353835613137653364393032383935396134333030623831
66333431326362303038343032316436356237373932316531313437613762636238663364356535
30393931613231303936366436316438396535303561393538633465623064623039386430393230
31336630663839336364336332623165613763303331383830663161613966623530323266396531
39393931363365366334643231363732646631626533653636373364393463333661653833653964
35656361623463396664306134313365333664616436313761313837363038343632373032356163
66653030393665626264663761313266383362393565633864636436326238343836383066363438
66656164373936663732333565313466366631336464316335663361663563363765333938653437
63346563356234333936666437396462613262633834333938343635613761396632633462633837
64303563376431643930343366626336656238616162366566613166623637656533653263383832
66643465626636303136383235383739393265313964373238393563613438346132383566353061
39393334643839613538343966383736646664373363373234376639636637326639383734313562
32306163326563366534303862386638643534636162353235626438373237343135376339663164
37313836313863646539396231353864623733396163636136313864613030326534663430633062
62373238313435346637623334623631343339323131643363383863323530333236313632653364
38346165396336623838623630336264313565396436646536393237646163333338346338653438
34396537373535616339626164356635333837623234316630373333646333343034376336383739
62346366383134626162376134643738623965653963303334353936613665313061653661616438
37643032393033313265396536353433323735613766326164323461623238316335623131343932
31313763303235366436613939366634646434326233303637653235323539626264306238353733
63383666636464313064623736316466323539376365376134663763343530333839313165643432
36663238333231306134313866303332623134323631646330373563656431363537323861353632
30306334366131633865633366353666306238326537633461323965303437656538643136386262
61386162626633343266666338363563663965643761636462663333373539396634313766323139
65653564333531373561393239343036616238363162633462353962393336396530383430353265
38373539613339313964356166666630376130313830356137326465653166633135346262613839
34623038396635373936333535343330623338663235653137393335346536343331383034666139
61333965386132666439616639623165663034383633303136393730396232336139306664663233
61613731646538633265323439666535346636333531326230366238653331303830646131343430
61383735386361356262326632663666383162316232393338363633323463613732623064623232
39623231643734363937623134336632623562356531333635633031636536383362343634373461
35633665383130636336363364616466346462663962376164336331663165316661393934396136
36623836636438373761376338303534353832653433396266376638636231616631356262323736
36383466643661363062373062663039333566633763316334626334373663633062376335663461
61316564303432333730333638303566313132646636643639626131343165663063333838333635
36663139643730333630633766383132396636666632366164346537363732353830613939663262
66623433633434646432313138643639366265396333626564623562323865616261663064363630
66313134356536646633366262356331336134366265316166633831653434646232333730356661
66663431653363666639333632366261613764393665393535373934616433653834646361373438
36363164363932663463623161373736353233393934633537613932333732376436623031303962
30643830353665616636663066306536336236373064376530306135646234326131613130306433
62653463306332346530336130623633393636393762633230323063646237626130353335336563
65646538313634333535333564663337623233613238303638616263383764653230343661343065
30626533643465333733366430316162316134323431643166316631623965323334316263396131
64303932663464383131386239336166353363333664353031646462363862663165376565356562
65346539633036343031396662623233613739656633653430663737613662303331396230353038
62333333383462653138653162626333326466653631643634636135663232393437343332333964
38623062656633313762353438313839373935373964393938396536633233613161666166626139
66323363323039323666303934373631343737313866343665643131386431373336333630346261
65646666303633623166643635326165303838646532393663666266303536343634633533346664
31386264656165376264653266663332316536303665323239613630386233373565353763323739
31373232623735366563303731626163356537393034313962653263333831363632343561316337
33663430333836633061643566303638316133323361343931303331353234613065366133363931
33316564313939383137653330353935396532386563623939363336383537386639376262376561
31323837346166396332373735316230653531343166393539386364646461376265373564636637
65663430333564393861343261316562386564393836303834633638346638383633353164303438
34343463353538663431306332636166623066376135323261613735393837363666666465323866
63356530346532656131383835363133353938636630333231353964373765636162613062363937
32393461356530383531363131326164356562383762643832353037393931316666353331396437
37616230383338613931646135306539636335636238373065346236333966393631346266343737
37383135613133383763306531326436323233393231376165633538653036393833373538653030
61336331313430326638373138376661633731306165653661313766663134383163653731393934
63643438353663646539623935356635303236616231646431356262383233643333393531376534
65663437633761646564333366626335336534626362353964316236643066306439366565373735
30373965366137643363653533366430303965636631383664303239623938616135303030373962
65346635663562306566303236663563623036366465353739626564393839643530353033323737
66643665303132346566333866663736376637393966366263636462396637663461646434376566
61353464313337333938303866663036646566623032393036646431343739646436356632353361
39366262376235663139666631643734376461613862613838653062313466663266646139323831
37313961383638353139353635633133636561306361323065323733633532333235323031653930
39306534343635646135646337353161303737396266343135363938383664346338373730366531
32396533323264666433616330373232623330383762323263333432383066313838663931333838
30636230333235653238356633353030663136343462353530643531376234336161656361633438
64336361636437636236373532346131393431373537633663396534363764653563393064313665
36616266333938643061613361653532356133663431333435343634373564656531383035353632
38383863616133363937633937643062623831356232346238326236313536393738346263313530
35303865666530393330343936356131326164653765623031323535363266393265333232333464
36356536613264353962323665333238666638373637306431343566656532383735363863313366
61666465316437383934653534363766356635666364383036366664356534333465366530633365
62353134313939373666623966666335663031616563373931386564383039613833323434326238
66373132323762653031363763353831353336396137376364383138366264396134343262306133
34393865653666663035633637373661366463303335353031373431386161616437353161326638
32633339356434633363313030636464376535383534346436663634396636376630613634653635
61383734313234396666303136393633363536326265643863656562356239326536353632363764
32383663306139373532643037303365346336643331336331663630333136323437663930333366
34366162356435316436313663633936323931383832373465323537653436623237323666333737
35303962396266336633623563663064613934613837356563636330323431346334326234663433
31313665653836616631653661626162613032313737373939323232386635393533363962373966
61396639366131323131623436633439363234333032636137646230656463306265333334393736
32323262643066306631316336356461633764646664653339626162366366353566373565653635
63363438396264666565383164363639303637646330333064363762393731613133633934666131
65323932373235373863623434373439343661396137656564396663653336396633393331663032
65363432373338313137356666623338303064393232636535356262383038323166393235653338
63613536626365666437376638633665626531366330313230663135353538303232356366326161
64303863643030316564643262303664653463653463393461313538343464313638653461643966
61653733343339613864666665373033353062363165376530323362663763306132336230396533
64373762626263626638643763336635616364323561633536326165326466393736333836376462
37303365396539376538663866633761313136326563623365373739373439393630376262373138
35663064636561626434313035346131633530326431653664373036356464313435333333386438
34396166643435663734363434316436386637643836313438333731323261303737653932666664
62313761383832373235353239313830326635633430313762656265313838623163366637633736
30316230353338626561393763663932663666613331653834346131343434663232623434353838
66396262383937353530366163313532343731353338633065393632353264343136386538616334
31383365666534303564616364343464313233643932326539313738386131646535316638396133
35636339396339323765336137626438643631653963333362356438663531626634633336303733
34626138336238323063623536333837336666613630626462636661353962303330323134326231
66396665383033386634373766383461616334383130356337323763353130653732333136303166
32303364616239616439336639633233666230396334666531643464653433333532313737363835
63623737383830646334643039643739636235333963643163336464373133333731633765393764
66616236346430323464393739326364333735663937643737356631393766623734636262336266
31376535613266613362313363666262383837663533316636323262316364633461656262363762
39613635343165326164646636343431623230353561363566666636656237373363646230363435
64343435633039336337313562353638363664373137396366303839303766316230323861396331
36346230353163336538303961626261383031366330613636323263346139356339616537393236
30306537663861323931323536373833643330313338363435383461383330656266613439333262
39643930313537386538323436636134643639656265336137666663666637363135643137333063
63346133313066643566643432356537383532326163356437386363353965303737656261656132
63386338643565383033353832663634633736323432336561383763356239303936333038396639
33616364346165323962313033666335323233343462353434663832386561376339666366393161
62656532636238613264643833336264616634623236643534363565303531656131646263613830
30323161623864386130316431313435383637316337333032633035303564636534326634646565
32333465303061626536303231386434363033636665366339376432376432383134353938663331
31323633386631383038376438313761323839646139616564383734366534376134366330336166
32323430663335653335376637313065383731383936663765303664356661636366616136656535
65346637313766326233376232646137313839616339636161613066653133613230623838326662
62313037393231643966353638316164333563343264616563633365333437356133373336386561
65323665356463326537326239396339613839306131656538323066656134333833656539623462
61386263646434636236643534636464363731656566383831656635336437363962306263633233
36646263636638396461313138306563656565613336356132393532626161343863633239636438
30393663323562623764663638646431336265303764356233323566613934306132333735623962
36626462343039313765333535633835303664393664366635393264373139346639636537313038
30383133376361303336363239653931653436663630666665383161333138346239616338313338
33343138666263623534396239633462383936646564313833373435346432323532336466373937
64386537653332646439643764643563393738323565346630616432316164663161313337366531
38396564386635323466386339613765326638333532623639303136656331633038336539393062
66613035306632373562666466306336353333333039346134383263313834666434313237376530
64373539383166363864366531323333323565366465383937333732613437366264396532616433
37623438653438656162626666343033346638636564653061396562343564626535616233353334
63373565383039383737333534663637666231323132313162373436666362383963303162373835
62656434313463336462643764333735653166306265636638326131613736393230326634333135
36666633626235343630306637386164313562323439663037386166303430393065386238383434
35396664336331336561633232333934343832626230343935393433326430653632393562366532
37346235343762336430653733353235643831633230646135333761613832336234646262623865
34386230623338613764303536343533623432336332396665626131363364643465666639636664
32376461393532313839393766623463386537643733616161313330623739626533643562303262
37356339353836303235343762393838343363623331646464646163363363663638626433636230
38323038363931353830633737383865353934373238313634613961373566336639326131653335
32623238313339396261333531373437336266363964386237366663336164323566313864396337
35636566616462353565383731386236356230303333336431666633613264373331663430346434
36663166363430373534346236363332346562653463373261346331653032613436616637623936
63393663666230663835623835633936656538323739356231353563623939383463353330326464
38373531653537376637633436616335633866656163303937636438643162653231393963356537
63306630313635363637656665373337363232336135346230643765376631343734303766633764
66356236643338356631373832323863353866336333316333626232396463303466643434646232
65623838353539353962346661383865626532613930363330326334316565343330343538636434
33626433623361313430313361323966353732303035626366323835383631343634316634623562
39313166376365373638303338326263616566643566636361633332373862623037336163636366
31626365663862303563626130393266316237643266383139643864613134373734663238633639
33613761383431383463383037313139393833633761633635356538313635383166386639326362
36346662626139353837376565633633363964343665356632646232393433326437353532613365
34323666633131653233393062366637336630303662306634363262666439353964653436363534
64656238363036343164656434343262393936306230333639393932663233363062666264656361
64363233303636646138323662623532333063646339306635633039653931333865356535396665
38346362366165336339646162623739653433353966613762373534306634643530316538356433
65393034366562663466313432396431636462383239326438643437316361343561393234306338
37333664666132343263343763333137316564363963306663363265376263393964386630646636
66316334323439663630373131643330636266633032376236383564643138356337353566363966
31323662613461353336396637366636366336353763666264626431653935663332363930303633
64643862393963613936323838653661646464353662613430336535363833663161323764303036
34396130633438323566366566303932363937653536653638303931646535353239393738323337
31316237356531373034646565653138396536326339376135613935366130333061636135613164
62386134626234623734333339393763393536623964343032316666366632633863643839353530
63366631646535323133353235613338383864613265613562653861653138333066396134653833
30313237646466333139323037376564646161613131393232333233633863396536363038396165
66643163386337333666626261336537353461666331353162346330616332616330653936616439
62356635323036323363653566313764396566346533646437323932366134303830356331303730
63363933396337653239343163366236386162333032356465326237623563386234656339643536
38393636376536616638303637393537393465656563353232623435323730353561303830363434
64346532303834363338643838346163313963303638346239373936303335373039323636343534
62623563343361613166633465373966343534633864303564393766656465393964396237353464
30666532393332323162313464663931356335333263643332373431343036636536316435303765
66633161653136653038366162353538623563653237666131383163333636373035643062626236
36303837376161363762353964363239656565396666356462633864343833373237303362316161
35323931616535633166646163616535363863616237363238316264363733636230323864363530
32353533666339656464303234366562616332633434393736663631656434373730313165313062
63373939313332346235386566613537343339363365623836313835343165386133643837336161
31666139393664363765646233323965653831333435376565653132313861613565393833613463
34623338313765386331643235303361363033393438353432383963663133306465613530323434
64306364326261303433623062313261613532303333656136363536353066313630363633353938
63623566613262646531616332376333633934666337376638616231653939646661316264376531
37346436323339633839393665383735653465313538366262366538623462306439363730386666
66383736623636333139346434663933303766333130323333383536316333363165633762336235
34613638626161326165346662613433316164323533376265353261303535336431323065623964
32636265653736363763316430636133376539376361616631643965353732656336333835356536
38373933623063316135353762663735376533663065336233363133613833373464633365346339
36613530663735663735353264373966643134306464343431633963366337616239623135663361
61303532323564306266656537636264663739356235393535633139646131346662353437306138
63313239623832376362353632326563633562663265623534643032306562343535613035656162
31353862366233663535396134643433306338333638613737623161376334653631316236383532
66316364313637633732363935636266393364636635373061353132383866306330333734616562
64633132336634643034633834623536383631363933653463663364643262623565393461613735
31343962333461393431643233396561376365666337313961616338313736373439323433393061
34636665323331653731346566656164316163666131396431333063656634373433306262666437
62363939326131663035633264613861376131623332666533653666373262613035636437346461
66336362393935633862323631393163373633306334366434346434333664313732636337313165
31356132646265346537363761393462393630653239396535383164323739636632306531626531
63643536366464383831333663376136643435636361666266333035616530333832336163636436
32613437333336323332376238623336373465323937353130643232656238643334313332303465
32306539626632323062343230666636653566376530633263373964623364363234316264346462
31656665336165643831333039376163623332633634643961316635396533356531323134356162
36363731373337376265303261313737663235306164666532303539656565343462333034366530
66643165646461303235653435613266383034333664323165646238616236663339633361383565
31616438623866633664323037343238383663343936643636333361633666336638333262316531
36663139313466303736336331326666666638333938613031353439313765343530626537393933
38383863643337363038386135326463363736373434646434343035613063343738383965366434
63613038353065353538323130323163383763383030666335383066373536336166393666333763
37663861353636633464396532666434313363323838346235643364393039366566313264333538
30353962613263623838656336353134373564383864383130356664376531626137353064643562
33393261346662346538666130303138623765383166626537636363313138646536623436643666
39626461383038633461306431363364636364623333663766396365653732333433346664316139
39333961366433633238386335343934383134663133633030386133386433633261383332306163
34373536346335616265666666386364646461653433386239653730656635663665643233323630
39616336386238333830363465636437323762633833633938323731653538613636653239373333
30636464343262356165313932356666663765376136663065666433616639613539323237303238
61386637373130626265373336326531653631343339613938376136346262626362653237633164
61616636626563613738613964376561376262343837396430653438623066663937653032326530
33396334653131356337376638356664313333343366373139623766393736326438376130646564
63333230306139303238323235353061616666643764333864646365356135376638643038313037
66316636643165333739356530663962346135323431386535356161393239616231326432393439
38326163363938373330386262386664313761613631356430633861346631623765663062643865
61353937383533633638313138316466313863656136393666353730303434613031313731333534
31306361613234626332386339653162393734623536613638343164663963326532623133663732
37343336326133626132383465306266623132643861376564636461383132306561613635373135
65386432316133393134386334653633666330623039336335396433623530633765383732333635
6431363665353131633562626461643631353938376163613361
31303035626165353032623535326564616362663265363634346230393632623233333132636230
3263666137666332353136626438663063633165663565360a333934333364653766626263383464
33356463653166666236623638303561376439373261306334373134386262636534623966633436
3330613861353861350a393035363566383862643563376436643665626135373830616231656365
61623530363761616533376434326230363132666433356238646139393730343264636362333463
63396239313137376262356664323562306634633064616166316331333862326436393861346632
33393031646532376462313837343762663266363366353665366266623434326538646239623234
65623362333166326566303739623466376537353561643034616563356664363266643462323435
63623436353162376366343836626439623963313764346562363061653832646130396463396530
35346631666633383435656563366164306432663131326633343563313134323937313961613437
32383163613439636561383064346238303535613339366234633234633430353232613761636562
36323461623465653765303533303230303064363762393865326533613336346366386630353339
61343164333262646239636161313231386133363037666539653666656461653666356239663234
66373736616232336466623030373366393366393032656138306465636431383937646461383136
66303733626633386335623835383135363265383062343937306564613234366435383236356537
66303638626365343066343537306636623935393637616237306337363538616262323735363230
34343561336634393334323763393836633736383739323661303531626133383834333138656362
35656463316136373861613636366165383530383465326666616139333130663230356166373633
62613535656161633530393031663063363031656431303338613864653739323932646333633236
36346464303564343661653138323433306563306566623966313664643030653934343230626333
38343732653264376530343933326365363864666539306562366434363161633132373165393234
65333339356330323562343032343334616439343963623066626538633238663132383965656231
38346230666230396434613036333466633334386337373437383063336364366130383064633831
38303630656162376331623335323565356530326539333536623835383132353462353161663061
37353263646537633561656133393562623034363830663961623331623263303861383465383865
66303765313830383431636236313937616666303833383738353438643461373166343130353661
31623166356134343833313736623738303837303332346361316532633434666266386661326337
64396633373537656135373938663433333230353632366538356135666139363232613664356436
36346363343638303764623931373263613461303266323766646434626336313933303237353538
35643165666232653831356530313330633831323461303165623436343861656431313266333662
31313461663438643062376262343365633534323837336434616431656131626530666637666432
63383461303562633063343863633832313530373334303234363366363966303866303138316131
32363034653836633636393864643966333032323830376465663937386238666539373733333838
38363964363135343764353432383736656561653164396361393337616335633635316536326333
36376164653565646138356433656639343938643039326165366461643230393364396439666266
62346163653531383934666562613563633163613239646265623236356132653230303032376364
37323532303666333964346132613262653236313239623531613933383362623233366632313736
39313162303930383637663237636237633539663763623661326530656132633233346361663136
34316462666661343839333634666130346662623136656536366661306433306134323163323561
31626264623939313033623738313265626637353837613735343166306233333837613336353639
61356461656561383565623965353738643033333637613039353838623337336138343937613663
64376163623562633135366563623533383466656234333535393030323631343163643363623463
35303131383937643632656434366339616266653134656163633661623330666637656335666335
61373362626463323131666231326636343866336337626637306465613463666335366431373339
32343136353463336164363664333861326465363334616530643234353165353235653665633135
64656163333462366437616337343438623835313139623839626237323433303861323963333937
64323437623630613136646161303363313033316634666531323534383363366232313166343433
61373832623464653865323364623837303637303234646462633336316630303766633733653131
35393834336166323334663033613938616232656134313735373434353263306130363130613966
34626130646534623864316630383235303263626462343732646633303764366234303135383638
38363362623137623532663365333563363138306336336537623236613766383861313865376131
61626231663930303162653334666231366435356231303730666664613737303961316163633339
34393730633966323565343039626231303736313836346237386662653537613132383962643035
62363233663366383365616632316161386531646230346331343162663537393033646330376538
37636234633136633363626634303662663765643664623432636530386364663961353330336164
63306339386364623331663930373335623766306536353663643130613731323261616339616661
33373138623333633962386161333166393466643565306564326234653165353362313038326538
62353136663533336361323966633737376133366338396431333435323261363035393561316136
36323738646138363861636439316531646235396466316138366266316361356664353533346630
61633538353661646437633433313463326332613261643536386666656637376163393266376530
65323834393131643731333863623134613638633235333064653238383363653432363736396462
38393939383139326362623937643364303833663861383130323235313865653831656133613439
63303533613065626434663066643531306139626133636532633932633037323536373439613461
61353234356162363936313065636136653636663437663536613563656333353039323863623861
61393630363237653963323534316130333363663561376133323039326137323766636161653831
65663937663434333337393863323134386638613435656130396366636139333130353334313762
63326661373932653561373635633332366332306133626133303066376137633531656562313835
30393266393161643836326664343731633935356264356136313362386535376466343333656264
64383663346365373830393333313735623536383732363037616231653164306337323832633661
37663437653763373563396465333836613964343536636564343665313534616230613162303338
66303337663361643635663639633832386263663539303361343934613864396534653135353766
33373862643332356530303964623839663732663536656134316262346334623361653266323263
31636133356635623166306463306637383333383235323334373736656262393661323564656166
36666161326436396133336662633465636532633262616132366162346365653433623639323830
61366466636165303563323738333831373931346563366561333235343539313930396637616461
61633561636136613665386162646130663864316238356130313931376238353234633137383734
36343231333266393030333630663863373663386333353039643661353861316366666339366634
66306334653339303762666135353032643034633739373330333830366436666138343738343534
64333137303963373438336361663136623064363665343835316661623831396532376362366134
34386463666532616131643331643436613630353561346430383736616437336466663232616165
32303434373035323235666564343136333161323839363361326162626465653736306332396530
33303637616637663566313665356335326266616534613234343839326138393664393062336430
36626533666137336366613761356438616136643436326437633936663961613434646263336439
34383538353063616464303931633561323834623761373763343962343663653265613030663036
32393538626530373838653934626333336161383235333265636665336638353839623061336133
30643439396638343261613831326131363837366462333031626666623031346236643437386532
35376133323462336262613563653830396461626362343861333139353038313966636363323836
65326466653365666338613566323461376333393162366638313733663631353933343962623737
38323365373263643138393339323937313864356131313032613463306135336134366230346339
31363966306339383264346464646361306463333031626534346662396336336338616563616338
37626434366161656238646433323133653430626564616336613461363661633138663932653038
30646435313436303937643730336434373666386163383439623338393862623838356537393836
65626165373363653638633164653761353265366461316239383732633837306636343137623962
33396435343837326338336538646534613131346463393862623234643363323030373164623533
61316466336132343433346538623761616535306564306566396339393830353235323735343463
64336462313630303631393839383833323337326337333166383431306638626166313064336336
39366163613634343933343462393932643666326434336666636262656633393762653536623838
61353438343165633038363135613665663531646261393534353938653262303763663166643737
62653935393338663065396462616534326237396434333566396466643238393734313036613532
61336663393537303031316161363165626661366166616333333761376532306462396463393932
35633266303665343836626335653238313065336435383634383566643162653337613531636165
64616635373533616230656335366131623262396163393561346432636363356132636339393861
65303735333935663232393162653739353964666164343434643737666538316633383930663232
63303230373233383261633863393638343562343831323834383163616333393762613265333961
61333032366433613032373263643662653066316162663438666666633430373135653738303763
62363139316638363463646631656266363333613966333365353666393233623336376338393931
66353438633435313061336435326336313734656230363061303530383937373965356563326462
31386661656533303861366333313939303337636264373432343466396633623533323662346536
39373865613639336265663862653735666239616331363531393138623763326363656437313963
65393934366430626464633632323738336636376533333963343536616135366131383934376138
36303562326463373261333538313135366435376138313539316236643334356636373063663930
62366534643333326362613163356532616266313436626162643132303266353962396365666162
66386231393161343561393864633566656361333439336663333763363339613439383231346236
63323635656635353135363535613134383465363331343730646239343936376333353636616639
63313439346262306634316236383138346166336365393531393930306139306637313831316666
37636133643839356366363532333466336438643739636136656338653533616231626431613439
37643565333863383633656233326432636662386437663731373564616135663661313265373430
65303830313161366437633233326136303330373163343736333835333736643035623062656136
66383436313165626234326438656665303831386563656330336332653130633964626633623764
30393933643362653039386332366463633363353834363866343061343236663738613061613739
39623736393364383635373964323163353338613466663366333631383531333736313265656566
31313537343039333164326232333166613238626533363832323732333261323864333532383737
63393664303362636662643130373565356364386362386261626431306631326633373230636531
35386134326435326539663138343532646533623130363163343164356461366662303833353732
65653638613437653835653139636563313263656464373166373163633861333761326562313436
32656130313938663433646265656566646434656337376136383837313263396537346130343536
31343466356463316338393862643063653331363434646563653238316532343764636538393461
38666365306532643437333164383661313365363831376233646165656633633236313962643233
33313763623430336132363639663734363762643732383866373762393533336162666361326638
64633231623338383633386636343338643138343362636636393239373231643031663865613331
62646366353439653465616337346363373736616137306339663433303234626536323633633233
65623131343039613366346665393235626364333063663830393436333633626464326465353832
30663561306138333831313033396165313063613063626563326562316636393036666638656134
30306665393137373865663161323435366365393731303764353539656266653035643964373462
37326639613161373436663335356666323964383737623133623962666138373465346462623635
30353664336664353539346264363563383330363964316332663734613566363035383735633333
33613830343633343631313362393037646564636333316231346166393935343739323962633636
39623063613631383561363030666131636437326537346333653739363434656334383638343635
33343763363234323137396239653239386533613263366565343865616131666331376465666562
32383130636462363861326264376362626264393666393366346165633131643734366130313334
37666336376433313833336131623234306631363832343039613333666134396461636565353432
37633633616464373639343064613635643466306661306535373935643563623163306336376637
33313638303436336537663463373535663133356362643763643430313462383538383939393133
33336632633835346139373731626430626561316366363665353039353134386265323634623861
65653837393465666230346437336538623765393131656638323238616537326432326531323166
38363530343335643637666262363763326466376265656534636135376135336563343735306162
31646231303932616439306666396537316161633032636638653238616637353738373539616664
66666661633137306331366230616531336461353963623934313635356338633030376461636261
61316561633366386661383666393961646264396537383439626262373833343036663365393339
36383862656331643035303635303935346366383534663563653237386435306439653931353566
63383964353666616433353833626132626362383065306561393539666637653834646633663865
33346365333262633362326537646633383031313536626462666363373236313663313264636335
39396330376366636339313133666437393833316161663264373265633962636338306534626335
38663962303130646239653439626366326661646239333235336635346131376264303562336130
31663165313665356639613937323165616631656535353564366265316637373135353366623332
39383333356636323763623630616562633364623638666665663831336665323031343037623930
36643762646139356661386262373134386431643536663038636236623636633364666333383135
33613638613261316333333033386461386332303363303138313031666264623039393935633731
32323937356639343834656465303938623338356633393233353630653039346534643366346535
62353134643563323966326434353764366436653036623161356239326162343966356164613533
35626138633537376637376334396535643132336631636137653666363264306166333733393831
32373563366135336463613734623565666365383266613931656238333631303530363965643938
39396330326339336336313236336432396262643066396133633036653938613133616531393036
35323265323966386465663331613062313763663131643663346465623031626662646133333734
64616131633665653135666562373666333362306233323539393234303866396333646536363238
31363633363431616263376339376238313162363437376530653366626439333364623764373864
30333832383532613836376361663934313561373733343166383266616236643034616662313933
65366637656338653437616166396637323633386334613461363062386630316135366433323935
31326232323361366563343039663837366337373862353833613336336633636635616565653164
30316531663432353238663037323866393766373866353766663434633937303032653362323664
30653334393439363638623539363337666161393532363933653961323662343761313430616538
35646563373563363761663635646338383566363731363264653330663635386232613565613264
33306266346361356632326332306534643865323032346330663966333964313535383261316161
61633535303739343763333031353736643664323731363839353934326132656364366566343462
30356631313636616165623662356162643261646139373366333433623661313164316664653539
66373137666130666535356435353738663133353864323537396433346261373131383764373863
62373836333962333665376539356632383361353561616334653139666363643937633837353839
36653034333930326135363565363662313731383135636231343666666536363934623034396131
31323166323763326231333063656139333730373030653631323365343637343536303365643965
66623733656165333030643139646439343030666633366534396337663236623763376232346465
39333839336538646262383034646335323066613231353731666132313734663637396563373332
34663462306565393934383436363865343932613336396238313231323636303765646164356565
65663463636331633336626438656334393366343338383564383231353364316361386265666566
63376464656263336461333661383333306566663130363939323131316663633662653738306366
35626361376163393864653332326463653564373563396531643532343766366261653332313131
33613463313136336434303039356662313233366137633062636232346365366361636633316565
34363562613265643437383033393062663165313536366634303661303533663862363234313839
30613032666164313231633837343532306331366437376530633364656531633534643335616364
31653764646236656637373036656532366631363164346262353034336564373233626263383333
37343166653865306565383036333032653335353161626630376436626363663065343336653936
30613339393064393532643862333530656432343631353766353338363464626535366164666661
33366634383339666135613430333832316262376162336530343363663666386431653232616664
30653637353432376132316561373031373330373266333731393338613635653466343464313334
63636435646466393766653135333037623638356630636239303564353337323135613233336461
30383537326139343538643633326331366531353732643730326465363566623265303030376534
34313635346134636337623737633838336239643938316264616435303935363036323339316466
66653238613463353964383231366563316666376237333631393435666133343636666435643535
38373635363134346636333563653063656537646331316264626139323631383039303564376461
32663633623538636232306532623261633563376562366133366665633862313431323032643666
65363362363065626536653430653562316539396264633865346233343430613333303865636139
66656632363564363739306666316666346239336432363762373730353637333465363337653466
62313765336130356532636261366232666462616464383439366334646364383861616166383836
33346162613132656262663062383962323637333332373062323936313365323864326439373565
64386235633531663366376332353866366333643736663833396361633836353061323333656132
36363761353566643432363634323861356534393664373761383565306436303239613532623337
66366666613932356138636230306434396365356338343437643534343634313432643238353234
63393338303961353264613666346339396439313838633431366134613931666565333263316632
37636365616532656339303963633135613830356130376463336335633331333065336538643963
34616565353630393932393231646433666363356134623363323462386136353139363931326237
66336637643530616630656231316662356138336266363434363463306236623462303764643162
31306634313630663530666464303337386664666361633539383531346533626335333939353432
35316664303938656436363631666465336561626431616663646239313938623961626230366533
31373961373332313264616433316366626464323462326462613736643137633031646536353766
66383465376262393133663931663835656236313334656563386266396237363963663430356137
31383563616165356539356530353566383431656163663130343865623639306165306531363966
33626638353532643063656132326239363166336334356236313334646538306162386530666131
33333935653933643331363636623738366263643162396131616364333963323534333263373439
38396231636532663934326465376161626565333732353037306131343763343162636539373763
30656135383263653238623062373231636436613733373131646433616336396531316230373937
39383231386438316432373035626639343266633335653835656262643433393434373333643863
36303833396363663430393164666439316465313662343162323435653632663737326435356239
38646531613439633737326539383361356237336561373264663838313535396236373033343535
35303234656666663766323261613739623930396239636639393936653537383432613165636137
34613964313136323430383564326265386135316339323962633830373931613037303436633361
62366438313738633263343833303237383239333534656538393432666332376461326631303637
30613037666237366666616163303662346230633131393938643636383636313436616539633337
63363332333662303761396339313766383132373831346266323234313338343430656136643638
66633964636337353736373464376631306139663539333464303438616334306666623061346537
33356665666661356363383066376665386266363434636262643430626130323163306634306633
37343735626361393036343566346230363137326563643666316630636139343865346430356131
61643535396264663336663866653762336330396135666162303665643733653630346565623236
35336630616531383931386430336238663963316237643765666534636264623533643930376637
37633031393532646362333733356165373431363330323863343362643231636361343862633666
35633834323137626137346230313662373166373039653532636163613231313737396262643364
38656465346336333965663463393963303136636131383432376465303463616564616631346139
33336535666133653563303039353737333162616239643261323333316334336335306230303037
32396339343137386632636636353730336464386537353530353563656464376532326133613933
33313734346132313066303538303761633333303564343262313636346238323236626566356535
34376232616335336539333662373563306637613232643732376635323036303666393737316139
34303663323064623863393262316533376263636332653964343235613338616137366461386132
39383837663538653533343663613963613066376432626464373061653139323139313937353031
34663961313435653263306263366366366632356236366162623239363336376166353137623136
37366434326539333535646339323738336238383835303163363334663737333135643564333763
61343236616530376335323537636666363537303130383064663666623461303339633564333836
33373031306639653063313063613763343332303861666533383239366463306538346566356466
65323161646264326330663164633239336562373564616131306330343765636339353237363134
36626462313962303132636463326534633035666335376133376131623863666563633066336537
65623264653066623566626534633662396163393438306239353864616565333531623939663961
63653461633565326463363165356664373164383631656131633663343336663137636561343363
61353339613033336462366131366565386338663435346462346136326634646134613234626666
64386465353962656532656631613735383664653932376334663262306230306130613936353732
36663538336634346531396438386437633038313830633164353664306533343837353332636530
30666231666532303437393264623030373461306266316335613465313531396436376563653932
63383764623635383634646163363435366430383864666632313437356135303934633662343366
32643834363937393162393666373361306332613233663564653633373935613661633131666432
66623931643436633964353433383264616462366235336266643630376532336566636632303038
65363838313539623132636464336632386164306632316230363136643038353135373635626164
33613239623438663832376137616530643762393666373063623734633164663961386666613137
36616638653038353235376138323666653866393035393937353631653532353734646663623932
39653162623833316262633033336363633030336561663231363730396535623738613834376636
35633434306438643937396532663364343661623134646239306362623432313930643333643834
35626231393431653238646635616161343733363662376638666166613835356236393135623439
31623465653532386438376235303036383434616135653438343431333832396563353066643161
65303361383437323032653033393663386338353164613137643662663231336332643639613530
34306535643161363064646432663961666537346464393530326665353237356134633162343839
31396466386335656236343639316136323536363231376632383364353236376339376535326164
36343334643433636238373363663435383966666133636164623863653232383064326237333234
38376435373066306462613935313735316232383364333733636261323835646237363262323862
39356465363132336235613465663762323230623133373237383961363936623763643430643235
31363931333964656539383739646234666433346663653965373833353633366566303864623863
63336330343666386135343730623432393934663532656565386531393762633734653438633461
65323366613334626134636631353631323238643138356164656461643866363061303062626433
64373662643532303266346333633366383133333339373865363137356632666232363039646538
61613130363763303563393735373761336164626432356664393063386162646233306166336563
31633739316662356630353938383861613964333661316237616533363530376230613464333265
38636133646235643438303163396132616537663630356635346332316364343966393131383931
31646536303761383866366366303131363634383062623731373434646430323237313730383531
61366533666162373962326132396166326564623464646136653730326430623631363762396231
61343333613162373865353165663331336232656139633766356239343162326664313639363135
39613639373736346531656636646136323237646232343139323164323665363664363665316630
30373164333166346437323661646463323032336265656339656336306431636537363433363533
34636464353332663138313132363739353065336564333532366634646238303435646237303533
61616231333832623036326133316534343566323134376461623338653836346435366433346438
39366563336436333466636433396230663136306666393334313039656365316264313561613863
32656238373536343132333135653131643936666231393165643662616434623532393638363666
34346634326631373739313463356632396366653161616461333432303333363265373561613266
30316264366465643633323237393562373963396135616465356533653464333536383935663738
62353537663663363862303038306461363364353133303533663732356366323631363138383632
62613662616538326435353534373865373231393833633566333534306231363734356435633835
36343962363739343235373834656135386662326136396331333964306633623931646539313232
33663162306462333562613632666431316330396630393165633530333737356635323633623430
64653231663665646436656263323032353530613731336331383131643937623333656138646664
35366638333562373431663038643331346634316431646130663666643464343037373266353966
34663736333839333835383437363562326436363962313066613864663665373438353932316330
62373066373634346434616234376535306133386331663438613061623965383333323364663664
32336438376262376331663565643466396237323362386261643162633636316138306331343764
39356361396535373961613930623030353766666133633738366263643830343463633563626462
30373839373862323164313265316139336364633836356534623835653633646439346436663966
30343137353765653239626265356338653031633166313432353865373530373437643531333339
32613636336333373239336230326639663466383466393930336161376663386534393866396161
61306563373637653962393233663130326535373737396361633236363239633333393838316633
34656435366662366666313632333739353962343132343265363962343236636439663866623961
32323935363538313531376637356636623666346535313939626333386564323765303961343961
35306234333738386266343333373733326634666362396432323561613437306533333536323337
38626439626338376536343439343431356338353264633661613035393636633863303133616464
35353861653463386634313437323639633965323432636164343862353030323033386566623963
32303962353632326239613537623834373534356238633433633131313763353662353036333132
34613236316331373937666631386462653539623135656533386164613064383666346239356635
65666164386635346166383963366135623934336636613963393862656131396466386335616161
63633334386665666165643664363065396665353061323063613635643864313330643433313661
36616632633037323338636630643037643334613236653137343530653032326335306238323766
62356266656235333434396435646332363639356163363365623136306432643131663131386139
39666137313563373462653638646230643762393962373963623161366233393462343036373738
63636464393263356331663030646433663737323233646461323963383335363962616337373934
39623865623835623163323164626162393038303931343036383738306463643431383364663564
36623563363363326665303731643531666434323338373935643736306138666665313035306136
37666230373034333261643431333262646238396436346366383063616531653961623834346336
30306336323665366131323462626330343136346334343435313630653135333637383731613963
66636537386336313235386234356238306361623962363263313039393362353665346664363637
63646433393464623663653431323133393235313834646266396265343633663732396638363532
63663336653638643638336566383239303563393764303065636138376336623166356634396432
64353763386230626566646634316437383963653462346435336536363462333038303966663931
38613732306431323937393336386133396362323939356330363132653736373433373834663433
66366333613263396131396434386565333065623335353661363336373530393934646630306337
62623062633038316530303865373662323332353065386430643530383335316565356135373666
32323731663461303138613064346138613333353534376232303538323132313039386264323732
61653033356337653735323330303462303439666464393262623062663736626661306139303837
34663330313539336663363830363963613561336664633330326265343333653839663462353230
39373938626266616239643939353937303635343661346535663564616136386337633533376137
61636135393836643764633838633361393230303730383932333564363739633137343930336331
63343937383763363436366335663763343534383963363266346637363437336635376566366230
36633535356239386238646136376431383061633939656663373735383235326663623365306438
64366433336638333235663437353463633937396136663764623166353864613162303832383566
38303664303962396362643631336536353264616335643332353137366431396464326635396634
38333038373933663236623632363431386337616566313765663633323163663933303337663938
32613133343730396666653461373161313139303063393639613932393864376337323363376332
66623266613962663066353335326230616438616562626337363965626564663666353064353530
61393934643933343132306265653235383430653837316365383265353565343762616135633866
64633766653133653335333061303233393038633665363566633238323530373239656661666161
64393262656162356665646661383861303765383232363639303065363061303332336562333732
31303363653136313732646362303838343838373533373338313838653633643136666631363638
31613738396463663931393064643132373564373837333337343832633837336663336262616665
63326134376538313466353333623633383861613534373534336464396261643233656130643765
31393631653630333233623339666535323031613939613835303764646161646132306635643161
37313361323165393034366365396139643233386637323734633463613931656238663537373562
37396265373364316561393433363635326365356338623038656439623539623139326235336632
32396264366634646330346232376636313038656164323134653866383864363864383965356666
65336363333035383637303934383533313663326431323637626133363338363538633634333035
34653961623933376534613239663037396432316237313137396339623239393635383966663164
65636266633439376135613832383634343633323366663833323533386239643762303638623738
35626335373135636261616138363739633134623738633966366139643365323264326239646636
37373231656436343431613830303662336138376437613938343439633534666563323930663938
65643036653265396237343164336365666337633361616433393561356537316438336331666365
63313964373235346534326639643264613539326366653938646663333365666538373530363064
63326431393130336165613632393662663563393066346562383534613833346661363933386135
63366636346539383636356662306162353161646132336630303739313866366434383464396136
39643163623433636164626563306138653265623563353262333831303438363739393762333464
32313664353563663364366636313366313639313134356332336562363864663862666632623832
66333236343136653439353066326434633032353139323563616666336239343730336530613761
62376231633237313732393835346665396634316263356630396463343137363936623064306461
39663935353463323565386333613464363438653237633666613261343833386336333261323561
33303662613239333362383661636235643861393734333463343464633930396434303562613034
36383130356635623665306362623235373364666137636336313066323933613836373431623635
30326465323366306566636637313631363963316566323039396430396139336234306330316465
34623339396136643938353066633036643837363562646539653533303035333562373436373031
30353438636430376265613038303633656230353934636163383465303363613936613236663366
63626637633432303463306666356562363065313335356530346539323961313035393865666633
36646332376238663166666264376132336637316561616532623534633430646139373532663138
66313066363435303864623866393462346137633730323066623337663335366461393632626138
38636635626365613561313334616230363936383231356465383439313138613136356462316634
63666131386339666464356533386333663562373733353434616666666639386365633732303737
30663364636336343362316135323236346639663939373365396561663766313362393838633737
36323835343731663235363234306332616336313035636235393038316438366538616162626634
36343836373966383538353034313264313732653635396233613835636461646231353365616163
35383835333466343934333065303538366361616436626338333137343332613965323964323836
66323665333461323864346635393636333163396334663264393337313863646139396664613163
30653637613864646631666364373162313939353339613732643637353635653665326164383261
30336135313766323933383232323630356139343934643863356434333663653830636561613731
65613861333639393434646436646131353563663864376564626439316261303161336336383739
32356635656637633066303638376436353261616232313065303431653036656235323337353933
39623930396239323766346362336131653137663533343133333937626535656236343639613363
62396338636336323531653564366133643866366432353566366661393263666164653630363738
65316562333131643966376634396335383131366233326231386432623364323235613635373635
35303931636466303130303233326531343539366238376363333138666234636532363965383239
62383734363734643165373734343963626538396237323530643333666131366365353538666662
37643939643661383138383638363431343330363931306162303963366230636364633066373537
39353462613265363439633961346331323864326536656536376534666534623936653231313263
30336232636434646666393566653164653834336563313236323965306663663431656239656163
66393136333937393863396531376561656538316164356265343532663262336539363662643962
61643031383037653964383661386238636633646232323565363162643239393330373263326432
30643561623733336234373962376239346663363537656462366134623563363762613462393333
34346431393136613866386338303066343463316233633933363636656630363638363264376263
38343564376564616665666432376336383833633765383263383137306235303934373230653862
34333233303037326432396365393339613261663266373465306635616435616363316563326436
38626532363862373832386335323335646462633138396162663134393036666166633337373638
36376562656434363835303336343638306538373733353062313530316363353161663732303836
63393363306336376564633462373265313539313130373434383539363462623131613862333936
62336630346133366264663665663431313437633762663530636266623935313766346233633438
36326164333839323562313930653038363262326539393932303237663563346664363032336630
38353530346166356231323062663666623961623835656265306561373332613937303739663965
61303765353437333465323165396438623838333635303164653764336633363633303935336264
33383934396630663564313163633466393861326330646461386536373336633135623030646266
37333134616531636135373063396330666538313736623563363432323936633163643162333466
31323739663931653235346235663832396437303932383066613633316362613563363530396463
64623363366237616632326531633932343231366430366633333633336462336634613033363830
36333661343765303336393263656231313739323038363038303436396130396335343539336561
65396532663637663234373431616536653661626665363634303265393931353232316236386433
63656266613965366439326630336533363166323935393230333930353166393465623438393830
39343332613462323437653865343333353338326636363735366462303766343433623136363739
35393630646537646333363931393764653036306261306633353463333939613837316265643961
62346234373039343066336534363065636339313031353264313537393063346566366263373339
65623539316635386532346435303631303030656262396535663433346138333963353732616562
32643334663139616439303639313333303064396563376361303933393437356664636331343631
33326565656632656638656331653435643433646533383036653264663232313730346331333139
33393238363165656137383130373137616132393366373930353161363232656532363936303038
34353463303666363730353235323034386230373333653263373238343863333233663261616431
34316135363537613762383036366333386165346361386165663939623639323535313237353365
63373135633538303232643065323232353434356365393464373963623066316138636661616632
34333332386432633933303465353564636361316435356163633765383833303437656535356130
62363863633433323231353932663534313166373734353837303861336162633438656237653137
31356330306236353432373431306631306561633264656663383266653266633661633237643461
34383566636162396234303233303534613461366466626135643336633635363037376638346338
65356334303633306536643966336561646531623633663664653461326237383331303232666666
33363765393334326130633634396163616639383339306137393364333233633539353263306663
38313866363737653932616339653230613830336132643361373933323466303038613831353066
30653963343637326364363537343437376661303937316464656234636161313235613839346464
38386636336262356561623566343931626237653663353137633935653830383033393035666565
62626239333731623337616636386361376565663966613562383639303338643737333063333161
61613937393865393037313932363639343562303561653633343266353237356666663966633331
33333430636439313464373632396163326531636231623066396462373738653366646238636535
37343461323565376339376362356364663239653463343362613966653337343733613366306431
64353862653365323336393330333863323564613664613838653036336466323239343161663430
63373039333433396238376134353666356632396434303166346134333562323862653361626134
33353138623863623739376134643561303565373261326133626630313035313563663763313131
31613832643063356261363639616265316332353864393764383532333538393938336433353333
62366334343139656139623165323935646439383437353636326666393935663333653533666132
39396539376263623834373137323430643538383336313932376166386465303361383166366463
30373466333663343835343730383331316536383261333934323166333765633339383161303630
65336631393638333638383431663035336162333062613630323130643663383135643161373736
31643465366539626136316565373666623631356434313633653633323533373531666265306235
62366237653136626238393462633165336532633734336662373130336538343162383366633166
63333333643038613561633137366233316432363836316333353839313432613837323364393031
32653165616432386565656666383737356430666239313363396263306335316435356232356238
32613032376536323130646439613832343364306431616337343730616239383730613139373365
37373638393264633935383166313130633864646635643734653832313037613132663666353666
33376466313838633530666136343132316530613662346431633063396165386133666230363333
66346630633366396137356430636330373562336638393936303438333531656661616161373939
35663037373332366262323731633166383336393236396461363535643535356138303266323862
39643332373534376234636133353735346662366435663930643464636135636633306366383835
38333137656264623765323239306365306661346466633638373735613735633063383139373464
34653934376565656438633235386433636434316565646634303332313636613662363661306138
31313562616364633832623934306461313739376236323334623761326335343334353561303762
66633937363234313039343534353435623035636234363462306363343035636536363262353165
33623930343234613964663332383833383433383537636163616137373134373032333439633937
38623033653437316336663931306235623264363934633139396132366430363133616163663033
63613631663963303635316161306131326137613439353564353438396564313635623030643935
39333866633130393062303332346566653461353036313134626131363766323632323465656363
32336131643466623139356639613035623531653061393666353431323638373735623066333564
63303664373765633830346534333439663963393163333764343162303265663465346130376164
61376631393438386430626663653936383330643131376431303136333030346466336439326466
64663962373731306462643662373166383333313164626135633136373930393734373932663663
34386235653037326332653432653435636132636263363938666561333436643734653264636431
31653439663264623837326561383464623163666237653433623433373336383931303066663265
34313663663966383133666534323366323739653336643437303564386363386363373033356464
35633364623062353561613331306463396162333037343037363966323330636131386530376338
33376234636462613239386134396335376563623039323033323064306537653136653239636161
38353766633562623135343461653433663338353939633330353462323737353536366564306134
39663034653039633463386230393032616638353737656438653662653833643861393235636331
30383039393130623362303533613535313365663434313662323633373039393762633731666232
37643635313865336637376666363432336563613735646433653337346566613764366466346365
646230383034313737303435323762633061
@@ -20,6 +20,17 @@
# multica_workspaces_root_mount: "/mnt/utilidor"
multica_agent_clis:
- claude
# Merged into the daemon user's ~/.claude/settings.json. Mirrors the model
# mix already live on haunted-mansion (unpinned runs on sonnet, subagents
# on haiku), and stops the npm install self-updating past the pin.
multica_claude_settings:
model: sonnet
alwaysThinkingEnabled: false
env:
DISABLE_AUTOUPDATER: "1"
CLAUDE_CODE_SUBAGENT_MODEL: haiku
MAX_THINKING_TOKENS: "4000"
BASH_MAX_OUTPUT_LENGTH: "20000"
# --- Secret source ----------------------------------------------------
# This homelab keeps Ansible secrets in ansible-vault encrypted files, so
@@ -45,6 +56,13 @@
# Environment variable on the control node:
# multica_pat: "{{ lookup('env', 'MULTICA_PAT') }}"
# 1Password service account token for `op` (WDWL-37). One shared token,
# vault_op_service_account_claude in group_vars/all/vault, scoped by
# 1Password to the agents' vault. The role writes it to a 0600 file and
# installs an `op` shim that reads it; it never reaches the daemon unit.
multica_op_deliver_token: true
multica_op_service_account_token: "{{ vault_op_service_account_claude }}"
# The role's own PAT assert is no_log, so a missing dictionary entry would
# surface there only as "censored". This checks key presence, never the
# value, so its failure can name the host.
+4
View File
@@ -6,6 +6,10 @@
# Directory layout on mad-tea-party
gitea_compose_dir: /opt/gitea
# Image versions
gitea_version: "28.2.0"
gitea_postgres_version: "17.11"
# Gitea application settings
gitea_domain: gitea.mk-labs.cloud
gitea_root_url: "https://{{ gitea_domain }}"
+3 -3
View File
@@ -15,9 +15,9 @@
# ── Compose & env ──────────────────────────────────────────────────────────────
- name: Deploy Gitea compose file from boilerplate
ansible.builtin.copy:
src: "{{ playbook_dir }}/../../boilerplates/gitea/compose.yaml"
- name: Deploy Gitea compose file
ansible.builtin.template:
src: compose.yaml.j2
dest: "{{ gitea_compose_dir }}/compose.yaml"
owner: root
group: root
@@ -0,0 +1,64 @@
# ------------------------------------------------------------------------------
# FILE: ansible/roles/gitea/templates/compose.yaml.j2
# DESCRIPTION: Gitea + PostgreSQL compose stack. Values come from role defaults.
# Postgres stays on 17.x; a major bump needs dump + restore.
# ------------------------------------------------------------------------------
services:
gitea:
image: docker.io/gitea/gitea:{{ gitea_version }}
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=gitea_db
- GITEA__database__NAME={{ gitea_db_name }}
- GITEA__database__USER={{ gitea_db_user }}
- GITEA__database__PASSWD=${DATABASE_PASSWORD}
- GITEA__server__SSH_PORT={{ gitea_ssh_port }}
- GITEA__server__ROOT_URL={{ gitea_root_url }}
- GITEA__server__DOMAIN={{ gitea_domain }}
- GITEA__server__HTTP_PORT={{ gitea_http_port }}
- GITEA__service__TRUSTED_PROXIES={{ gitea_trusted_proxies }}
networks:
- gitea_backend
ports:
- "{{ gitea_http_port }}:{{ gitea_http_port }}"
- "{{ gitea_ssh_port }}:22"
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
depends_on:
gitea_db:
condition: service_healthy
gitea_db:
image: docker.io/library/postgres:{{ gitea_postgres_version }}
restart: unless-stopped
environment:
- TZ={{ gitea_timezone }}
- POSTGRES_USER={{ gitea_db_user }}
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
- POSTGRES_DB={{ gitea_db_name }}
networks:
- gitea_backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U {{ gitea_db_user }}"]
start_period: 30s
interval: 10s
timeout: 10s
retries: 5
volumes:
- gitea_db:/var/lib/postgresql/data
volumes:
gitea_data:
driver: local
gitea_db:
driver: local
networks:
gitea_backend:
driver: bridge
+87 -3
View File
@@ -37,6 +37,8 @@ otherwise clean at the `production` profile.
| Node.js, pinned, from nodejs.org with SHASUMS256 verification | yes, when an npm-installed agent CLI is selected |
| `multica` CLI, pinned, from the GitHub release with `checksums.txt` verification | yes |
| Agent CLIs (`claude`, `hermes`, …) | yes |
| 1Password CLI (`op`), from 1Password's signed apt/yum repository | yes (`multica_op_install`) |
| 1Password service account token, as a 0600 file for the daemon user, plus an `op` shim that reads it | opt-in (`multica_op_deliver_token`); not exported to the daemon |
| `workspaces_root`, on the intended mount, writable by the daemon user | yes |
| Multica authentication (`multica login --token`) | yes |
| CLI config (`server_url`, `app_url`, `workspaces_root`, `device_name`, …) | yes |
@@ -236,6 +238,10 @@ See `defaults/main.yml` for the annotated list. The ones you will actually set:
| `multica_user` | `multica` | Daemon service account. |
| `multica_profile` | `""` | `--profile`, to isolate config/daemon state/workspaces. |
| `multica_allow_remote_install_scripts` | `false` | Required for `hermes`; see below. |
| `multica_op_install` | `true` | 1Password CLI from 1Password's apt/yum repo. |
| `multica_op_version` | `""` | `""` tracks the repo (upgrades on every run); set e.g. `2.41.1` to pin. |
| `multica_op_deliver_token` | `false` | Write `multica_op_service_account_token` to `multica_op_token_file`. |
| `multica_op_shim` | `true` | With the token delivered, install the `op` shim at `multica_op_shim_path`. |
### Adding an agent CLI
@@ -307,13 +313,19 @@ Every step reads before it writes:
| CLI install | `multica --version` equals the pin |
| Agent CLI install | `command -v <binary>` and its version command |
| Config | `multica config show` parsed and compared key by key |
| Login | `multica auth status` already names `multica_server_url` |
| Login | `multica auth status` already names `multica_server_url` (it prints to **stderr**; the guard reads both streams) |
| Autostart | `multica daemon autostart status` |
| Daemon start | `multica daemon status --output json` reports `running` |
| Daemon start | `systemctl --user is-active <unit>`; a daemon running *outside* the unit is stopped first, then the unit is started |
A binary, config or credential change notifies a `multica daemon restart`
A binary, config or credential change notifies a `systemctl --user restart`
handler, which is flushed *before* verification reads the daemon's state back.
The role never calls `multica daemon start` or `multica daemon restart`. Both
fork the daemon inside the caller's session (Ansible's SSH session scope),
outside the unit's cgroup. The unit then shows `inactive`, nothing supervises
the process, and a later `systemctl --user start` collides with it on
`daemon.lock`.
A converged host reports zero changed tasks.
## Verification
@@ -360,6 +372,77 @@ ssh -t scratch-vm 'sudo -iu multica claude'
That step only becomes automatable if the host moves from a subscription to an
API key.
## 1Password CLI
`op` lets an agent resolve `op://vault/item/field` references at the moment
it runs a command (`op run -- cmd`, `op inject -i tpl -o out`, `op read`), so
the secret value never has to appear in a prompt, an issue or a transcript.
The 1Password desktop-app integration (and the Claude Code plugin built on it)
needs a GUI session, so headless agent hosts use a **service account** instead.
Install, Debian/Ubuntu, exactly as 1Password documents it, with one addition:
- the key at `multica_op_key_url` is fingerprint-checked against
`multica_op_key_fingerprint` before it is dearmored, so apt never trusts a
substituted key;
- dearmored to `/usr/share/keyrings/1password-archive-keyring.gpg` (apt,
`signed-by=`) and to `/usr/share/debsig/keyrings/AC2D62742012EA22/debsig.gpg`
with the policy in `/etc/debsig/policies/AC2D62742012EA22/` (debsig-verify);
- `1password-cli` from `downloads.1password.com/linux/debian/<arch> stable`.
EL uses 1Password's yum repository with `gpgcheck` and `repo_gpgcheck`.
The play then runs `op --version` as the daemon user, in the daemon user's
environment.
### The service account token
The role can deliver the token, but by default does not:
```yaml
multica_op_deliver_token: true
multica_op_service_account_token: "{{ vault_op_service_account_claude }}"
```
writes it to `multica_op_token_file`
(`~multica/.config/op-service-account/token`, directory 0700, file 0600,
owned by the daemon user) and installs an `op` shim. **The role does not put
the token into the daemon's environment**, for two reasons:
1. The daemon's systemd user unit is written and owned by the multica CLI
(`multica daemon autostart`). The role must not template or edit it, or it
silently freezes the CLI's `ExecStart`, environment and restart semantics.
2. Anything in the daemon's environment is inherited by every agent process
it spawns, where a casual `env` or `printenv` puts it in a transcript.
`op` reads the token only from `OP_SERVICE_ACCOUNT_TOKEN`; it has no
token-file option. So the role installs a shim at `multica_op_shim_path`
(`~multica/.local/bin/op`, root-owned 0755), which the CLI-written unit
already puts first on `PATH`. The shim:
- reads the token file and exports `OP_SERVICE_ACCOUNT_TOKEN` to
`multica_op_binary` (`/usr/bin/op`) only, then `exec`s it. Agents call plain
`op run` / `op inject` / `op read` and the token never enters their shell;
- fails closed (exit 78, no token in the message) when the file is missing
or empty;
- for `op run` and `op plugin run`, splices `/usr/bin/env -u
OP_SERVICE_ACCOUNT_TOKEN` in after the first `--`. `op run` otherwise
hands its whole environment, token included, to the command it starts.
Because it needs that `--` to find the command, the shim refuses
`op run <command>` without one (exit 64).
The play checks, as the daemon user, that `op` resolves to the shim and that
the token is not in that environment.
The shim keeps the token out of transcripts by accident; it is not a
boundary. Any process running as the daemon user can read the token file on
purpose. What actually limits exposure is the service account's vault access
in 1Password: keep it read-only. Check it as the daemon user:
```bash
op vault list --permission read_items # vaults it can read
op vault list --permission edit_items # should be empty
```
## Known gaps
- **Provider CLI login.** Unavoidable for subscription auth.
@@ -381,6 +464,7 @@ API key.
| `multica_auth` | `multica login` |
| `multica_daemon` | autostart + start |
| `multica_verify` | the assertions |
| `multica_op` | 1Password CLI, and the token file and shim when enabled |
| `multica` | all of the above |
Preflight and the service-account step are tagged `always`: they build
@@ -129,6 +129,48 @@ multica_agent_cli_catalog:
manual_login: true
manual_login_hint: "configure Hermes credentials as {{ multica_user }} (Hermes reads 1Password natively; confirm against its docs)"
# Keys MERGED (recursively) into the daemon user's ~/.claude/settings.json.
# Merged, not templated: Claude Code writes that file itself. Keys not named
# here are left alone. Empty = don't touch the file.
#
# An npm-installed Claude Code updates itself in place, which silently breaks
# the package_version pin above (and the next play downgrades it again).
# Setting env.DISABLE_AUTOUPDATER: "1" here is what makes the pin hold.
multica_claude_settings: {}
# -----------------------------------------------------------------------------
# 1Password CLI (op)
# -----------------------------------------------------------------------------
# Lets agents resolve op:// references with `op run` / `op inject` / `op read`,
# so secret values never pass through a prompt or a transcript. Installed from
# 1Password's signed apt/yum repository.
multica_op_install: true
# "" = whatever the repository serves, upgraded on every run. Set e.g. "2.41.1"
# to pin. 1Password's apt index carries only the newest build, so a pin older
# than that cannot be installed on a fresh host.
multica_op_version: ""
multica_op_key_url: "https://downloads.1password.com/linux/keys/1password.asc"
# Code signing for 1Password <codesign@1password.com>. The play refuses a key
# at multica_op_key_url with any other fingerprint.
multica_op_key_fingerprint: "3FEF9748469ADBE15DA7CA80AC2D62742012EA22"
multica_op_debsig_policy_url: "https://downloads.1password.com/linux/debian/debsig/1password.pol"
multica_op_apt_base_url: "https://downloads.1password.com/linux/debian"
multica_op_rpm_base_url: "https://downloads.1password.com/linux/rpm/stable"
# Service account token delivery. Off by default. When on, the token is written
# to multica_op_token_file (0600, owned by the daemon user) and read from there
# by the op shim below. The role does NOT export it into the daemon
# environment; see the README. Supply the VALUE from the playbook, e.g.
# multica_op_service_account_token: "{{ vault_op_service_account_claude }}"
multica_op_deliver_token: false
multica_op_service_account_token: ""
multica_op_token_file: "{{ multica_user_home }}/.config/op-service-account/token"
# With the token delivered, install a shim at multica_op_shim_path (first on
# the daemon unit's PATH) that exports the token to multica_op_binary only.
multica_op_shim: true
multica_op_shim_path: "{{ multica_user_home }}/.local/bin/op"
multica_op_binary: /usr/bin/op
# -----------------------------------------------------------------------------
# Node.js (installed only when a selected agent CLI needs npm)
# -----------------------------------------------------------------------------
@@ -1,7 +1,9 @@
---
# Through systemd, not `multica daemon restart`: that respawns the daemon in
# the caller's session, outside the unit (see tasks/daemon.yml).
- name: Restart multica daemon
ansible.builtin.command:
argv: "{{ _multica_cmd + ['daemon', 'restart'] }}"
argv: [systemctl, --user, restart, "{{ _multica_unit | default('multica-daemon.service') }}"]
become: true
become_user: "{{ multica_user }}"
environment: "{{ _multica_user_env }}"
@@ -1,6 +1,10 @@
---
# `multica auth status` prints the server and user when authenticated and exits
# non-zero otherwise, so it is the idempotency guard for login.
#
# It prints to STDERR, not stdout (0.6.5). A stdout-only guard never matches,
# so every run re-logs in and restarts the daemon. It also echoes a token
# prefix, hence no_log.
- name: Auth | Read the current authentication state
ansible.builtin.command:
argv: "{{ _multica_cmd + ['auth', 'status'] }}"
@@ -11,13 +15,15 @@
changed_when: false
failed_when: false
check_mode: false
no_log: true
- name: Auth | Decide whether a login is needed
ansible.builtin.set_fact:
_multica_needs_login: >-
{{ multica_force_relogin | bool
or _multica_auth_status.rc != 0
or multica_server_url.rstrip('/') not in (_multica_auth_status.stdout | default('')) }}
or multica_server_url.rstrip('/') not in
((_multica_auth_status.stdout | default('')) ~ (_multica_auth_status.stderr | default(''))) }}
# no_log is mandatory: without it Ansible records the full argv — including the
# PAT — in play output, CI logs, and every callback plugin.
@@ -0,0 +1,34 @@
---
- name: Claude settings | Ensure ~/.claude exists
ansible.builtin.file:
path: "{{ multica_user_home }}/.claude"
state: directory
owner: "{{ multica_user }}"
group: "{{ multica_group }}"
mode: "0700"
become: true
- name: Claude settings | Read the current settings
ansible.builtin.slurp:
src: "{{ multica_user_home }}/.claude/settings.json"
register: _multica_claude_settings_raw
failed_when: false
become: true
- name: Claude settings | Merge the declared keys over them
ansible.builtin.set_fact:
_multica_claude_settings_current: >-
{{ (_multica_claude_settings_raw.content | b64decode | from_json)
if _multica_claude_settings_raw.content is defined else {} }}
- name: Claude settings | Write them back if anything differs
ansible.builtin.copy:
dest: "{{ multica_user_home }}/.claude/settings.json"
content: "{{ _multica_claude_settings_current | combine(multica_claude_settings, recursive=true) | to_nice_json }}\n"
owner: "{{ multica_user }}"
group: "{{ multica_group }}"
mode: "0600"
become: true
when: >-
_multica_claude_settings_current | combine(multica_claude_settings, recursive=true)
!= _multica_claude_settings_current
@@ -43,8 +43,61 @@
- multica_autostart | bool
- _multica_autostart_status.rc != 0
or 'enabled' not in (_multica_autostart_status.stdout | default('') | lower)
register: _multica_autostart_enable
changed_when: true
# Lifecycle goes through systemd, never `multica daemon start|restart`. Those
# fork the daemon inside the CALLER's session (here, Ansible's SSH session
# scope), outside the unit's cgroup: the unit stays inactive, nothing
# supervises the process, and the next `systemctl --user start` collides with
# it on daemon.lock. Seen on haunted-mansion (2026-10-21) and spaceship-earth
# (2026-10-22).
#
# The unit name comes from `autostart status` ("Location: .../<name>.service")
# so a --profile host resolves its own unit.
- name: Daemon | Re-read the autostart registration
ansible.builtin.command:
argv: "{{ _multica_cmd + ['daemon', 'autostart', 'status'] }}"
become: true
become_user: "{{ multica_user }}"
environment: "{{ _multica_user_env }}"
register: _multica_autostart_reread
changed_when: false
failed_when: false
check_mode: false
when: _multica_autostart_enable is changed
- name: Daemon | Resolve the systemd user unit
ansible.builtin.set_fact:
_multica_unit: >-
{{ ((_s.stdout | default('')) ~ '\n' ~ (_s.stderr | default('')))
| regex_search('Location:\s*\S*/([^/\s]+\.service)', '\1')
| default(['multica-daemon.service'], true) | first }}
vars:
_s: "{{ _multica_autostart_reread if _multica_autostart_enable is changed else _multica_autostart_status }}"
# `autostart enable` may rewrite the unit file while the user manager still
# holds the old definition.
- name: Daemon | Reload the user manager after a (re)registration
ansible.builtin.command:
argv: [systemctl, --user, daemon-reload]
become: true
become_user: "{{ multica_user }}"
environment: "{{ _multica_user_env }}"
when: _multica_autostart_enable is changed
changed_when: true
- name: Daemon | Read the unit state
ansible.builtin.command:
argv: [systemctl, --user, is-active, "{{ _multica_unit }}"]
become: true
become_user: "{{ multica_user }}"
environment: "{{ _multica_user_env }}"
register: _multica_unit_state
changed_when: false
failed_when: false
check_mode: false
- name: Daemon | Read daemon status
ansible.builtin.command:
argv: "{{ _multica_cmd + ['daemon', 'status', '--output', 'json'] }}"
@@ -56,15 +109,27 @@
failed_when: false
check_mode: false
- name: Daemon | Start it if it is not already running
# Running, but not under the unit: an orphan. Stop it so the unit can own the
# lock and the process.
- name: Daemon | Stop a daemon running outside its unit
ansible.builtin.command:
argv: "{{ _multica_cmd + ['daemon', 'start'] }}"
argv: "{{ _multica_cmd + ['daemon', 'stop'] }}"
become: true
become_user: "{{ multica_user }}"
environment: "{{ _multica_user_env }}"
when: >-
_multica_daemon_status.rc != 0
or ((_multica_daemon_status.stdout | from_json).status | default('')) != 'running'
when:
- (_multica_unit_state.stdout | trim) != 'active'
- _multica_daemon_status.rc == 0
- ((_multica_daemon_status.stdout | from_json).status | default('')) == 'running'
changed_when: true
- name: Daemon | Start the unit if it is not active
ansible.builtin.command:
argv: [systemctl, --user, start, "{{ _multica_unit }}"]
become: true
become_user: "{{ multica_user }}"
environment: "{{ _multica_user_env }}"
when: (_multica_unit_state.stdout | trim) != 'active'
changed_when: true
# Anything that changed the binary, the config or the credentials restarts the
@@ -27,6 +27,23 @@
ansible.builtin.import_tasks: agent_clis.yml
tags: [multica, multica_agents]
- name: Claude Code settings
ansible.builtin.import_tasks: claude_settings.yml
when:
- "'claude' in multica_agent_clis"
- multica_claude_settings | length > 0
tags: [multica, multica_agents, multica_claude_settings]
- name: 1Password CLI
ansible.builtin.import_tasks: op_cli.yml
when: multica_op_install | bool
tags: [multica, multica_op]
- name: 1Password service account token
ansible.builtin.import_tasks: op_token.yml
when: multica_op_deliver_token | bool
tags: [multica, multica_op]
- name: CLI configuration
ansible.builtin.import_tasks: config.yml
tags: [multica, multica_config]
@@ -0,0 +1,187 @@
---
# 1Password CLI from 1Password's own signed package repository, never a
# curl-piped binary. Mirrors https://developer.1password.com/docs/cli/get-started
# (Linux, APT / YUM), with the signing key's fingerprint checked before apt or
# dnf is allowed to trust it.
- name: 1Password CLI | Require a supported OS family
ansible.builtin.assert:
that:
- ansible_facts['os_family'] in ['Debian', 'RedHat']
fail_msg: >-
The 1Password CLI install supports Debian- and RedHat-family hosts only
(found {{ ansible_facts['os_family'] }}). Set multica_op_install: false.
quiet: true
# --- Debian / Ubuntu ---------------------------------------------------------
- name: 1Password CLI | Install gnupg and debsig-verify
ansible.builtin.apt:
name: [gnupg, debsig-verify]
state: present
become: true
when: ansible_facts['os_family'] == 'Debian'
- name: 1Password CLI | Fetch the signing key
ansible.builtin.get_url:
url: "{{ multica_op_key_url }}"
dest: /usr/share/keyrings/1password-archive-keyring.asc
owner: root
group: root
mode: "0644"
register: _multica_op_key_asc
become: true
when: ansible_facts['os_family'] == 'Debian'
- name: 1Password CLI | Read the signing key's fingerprint
ansible.builtin.command:
argv: [gpg, --show-keys, --with-colons, /usr/share/keyrings/1password-archive-keyring.asc]
register: _multica_op_key_info
changed_when: false
check_mode: false
become: true
when:
- ansible_facts['os_family'] == 'Debian'
- not (ansible_check_mode and _multica_op_key_asc is changed)
# Under --check on a fresh host the key was only pretend-downloaded, so there is
# nothing to read; the real run checks it.
# A key that does not match is never dearmored, so apt never trusts it.
- name: 1Password CLI | Require the expected signing key
ansible.builtin.assert:
that:
- multica_op_key_fingerprint in (_multica_op_key_info.stdout_lines
| select('match', '^fpr:') | map('split', ':') | map(attribute=9) | list)
fail_msg: >-
{{ multica_op_key_url }} is not the key with fingerprint
{{ multica_op_key_fingerprint }}. Refusing to trust it.
quiet: true
when:
- ansible_facts['os_family'] == 'Debian'
- _multica_op_key_info is not skipped
- name: 1Password CLI | Look for the dearmored keyrings
ansible.builtin.stat:
path: "{{ item }}"
loop:
- /usr/share/keyrings/1password-archive-keyring.gpg
- /usr/share/debsig/keyrings/AC2D62742012EA22/debsig.gpg
register: _multica_op_keyrings
become: true
when: ansible_facts['os_family'] == 'Debian'
- name: 1Password CLI | Create the debsig policy and keyring directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- /etc/debsig/policies/AC2D62742012EA22
- /usr/share/debsig/keyrings/AC2D62742012EA22
register: _multica_op_debsig_dirs
become: true
when: ansible_facts['os_family'] == 'Debian'
# Same key, two consumers: apt (repository metadata) and debsig-verify (the
# signature on each .deb).
- name: 1Password CLI | Dearmor the key for apt and debsig
ansible.builtin.command:
argv: [gpg, --batch, --yes, --dearmor, --output, "{{ item.item }}", /usr/share/keyrings/1password-archive-keyring.asc]
loop: "{{ _multica_op_keyrings.results }}"
loop_control:
label: "{{ item.item }}"
when:
- ansible_facts['os_family'] == 'Debian'
- _multica_op_key_asc is changed or not item.stat.exists
changed_when: true
become: true
- name: 1Password CLI | Install the debsig policy
ansible.builtin.get_url:
url: "{{ multica_op_debsig_policy_url }}"
dest: /etc/debsig/policies/AC2D62742012EA22/1password.pol
owner: root
group: root
mode: "0644"
become: true
when:
- ansible_facts['os_family'] == 'Debian'
- not (ansible_check_mode and _multica_op_debsig_dirs is changed)
- name: 1Password CLI | Add the apt repository
ansible.builtin.apt_repository:
repo: >-
deb [arch={{ _multica_arch }} signed-by=/usr/share/keyrings/1password-archive-keyring.gpg]
{{ multica_op_apt_base_url }}/{{ _multica_arch }} stable main
filename: 1password
state: present
update_cache: true
register: _multica_op_repo_deb
become: true
when: ansible_facts['os_family'] == 'Debian'
- name: 1Password CLI | Install the package
ansible.builtin.apt:
name: >-
{{ '1password-cli=' ~ multica_op_version ~ '*'
if multica_op_version | length > 0 else '1password-cli' }}
state: "{{ 'present' if multica_op_version | length > 0 else 'latest' }}"
allow_downgrade: "{{ multica_op_version | length > 0 }}"
register: _multica_op_pkg_deb
become: true
when:
- ansible_facts['os_family'] == 'Debian'
- not (ansible_check_mode and _multica_op_repo_deb is changed)
# --- EL ----------------------------------------------------------------------
- name: 1Password CLI | Add the yum repository
ansible.builtin.yum_repository:
name: 1password
description: 1Password Stable Channel
baseurl: "{{ multica_op_rpm_base_url }}/$basearch"
gpgkey: "{{ multica_op_key_url }}"
gpgcheck: true
repo_gpgcheck: true
enabled: true
register: _multica_op_repo_rpm
become: true
when: ansible_facts['os_family'] == 'RedHat'
- name: 1Password CLI | Install the package
ansible.builtin.dnf:
name: >-
{{ '1password-cli-' ~ multica_op_version
if multica_op_version | length > 0 else '1password-cli' }}
state: "{{ 'present' if multica_op_version | length > 0 else 'latest' }}"
allow_downgrade: "{{ multica_op_version | length > 0 }}"
register: _multica_op_pkg_rpm
become: true
when:
- ansible_facts['os_family'] == 'RedHat'
- not (ansible_check_mode and _multica_op_repo_rpm is changed)
# --- Verify ------------------------------------------------------------------
# As the daemon user, through its own environment: that is the context agents
# call `op` from.
- name: 1Password CLI | Check the installed version as the daemon user
ansible.builtin.command:
argv: [op, --version]
become: true
become_user: "{{ multica_user }}"
environment: "{{ _multica_user_env }}"
register: _multica_op_version_out
changed_when: false
check_mode: false
failed_when: >-
_multica_op_version_out.rc != 0
or (multica_op_version | length > 0
and _multica_op_version_out.stdout | trim != multica_op_version)
# Under --check, only when the binary is already there to ask.
when: >-
not ansible_check_mode
or (_multica_op_pkg_deb is not skipped and _multica_op_pkg_deb is not changed)
or (_multica_op_pkg_rpm is not skipped and _multica_op_pkg_rpm is not changed)
@@ -0,0 +1,81 @@
---
# Delivers the 1Password service account token to a file only the daemon user
# can read. It deliberately stops there: nothing here puts the token into the
# daemon's environment. The daemon's systemd user unit belongs to the multica
# CLI, so the role must not edit it, and exporting the token to the daemon
# would hand it to every agent process too. See the README.
# no_log: a failed assert would otherwise render the token it inspected.
- name: 1Password token | Require a service account token
ansible.builtin.assert:
that:
- multica_op_service_account_token | length > 0
- multica_op_service_account_token is match('^ops_')
fail_msg: >-
multica_op_deliver_token is true but multica_op_service_account_token is
not a 1Password service account token (ops_...).
quiet: true
no_log: true
- name: 1Password token | Create the token directory
ansible.builtin.file:
path: "{{ multica_op_token_file | dirname }}"
state: directory
owner: "{{ multica_user }}"
group: "{{ multica_group }}"
mode: "0700"
become: true
- name: 1Password token | Write the token file
ansible.builtin.copy:
dest: "{{ multica_op_token_file }}"
content: "{{ multica_op_service_account_token }}\n"
owner: "{{ multica_user }}"
group: "{{ multica_group }}"
mode: "0600"
no_log: true
become: true
# --- op shim -----------------------------------------------------------------
# `op` reads the token only from OP_SERVICE_ACCOUNT_TOKEN. The shim, first on
# the daemon unit's PATH, exports it for the real binary alone, so agents call
# plain `op` and the token never enters their shell environment. The daemon
# unit itself stays the CLI's (WDWL-18).
- name: 1Password token | Create the daemon user's bin directory
ansible.builtin.file:
path: "{{ multica_op_shim_path | dirname }}"
state: directory
owner: "{{ multica_user }}"
group: "{{ multica_group }}"
mode: "0755"
become: true
when: multica_op_shim | bool
- name: 1Password token | Install the op shim
ansible.builtin.template:
src: op-shim.sh.j2
dest: "{{ multica_op_shim_path }}"
owner: root
group: root
mode: "0755"
become: true
when: multica_op_shim | bool
# As the daemon user, through its own environment: plain `op` must resolve to
# the shim, and the token must not be in that environment.
- name: 1Password token | Check op resolves to the shim as the daemon user
ansible.builtin.shell:
cmd: 'command -v op; env | grep -c "^OP_SERVICE_ACCOUNT_TOKEN=" || true'
become: true
become_user: "{{ multica_user }}"
environment: "{{ _multica_user_env }}"
register: _multica_op_shim_check
changed_when: false
check_mode: false
failed_when: >-
_multica_op_shim_check.rc != 0
or _multica_op_shim_check.stdout_lines != [multica_op_shim_path, '0']
when:
- multica_op_shim | bool
- not ansible_check_mode
@@ -0,0 +1,55 @@
#!/bin/sh
# {{ ansible_managed }}
#
# 1Password CLI shim for the Multica daemon user (multica_agent_host role,
# WDWL-37). Reads the service account token from a 0600 file and exports it
# to {{ multica_op_binary }} only, so it never sits in an agent's shell
# environment.
token_file="{{ multica_op_token_file }}"
if [ ! -r "$token_file" ]; then
echo "op: no 1Password service account token at $token_file" >&2
exit 78
fi
IFS= read -r OP_SERVICE_ACCOUNT_TOKEN < "$token_file"
if [ -z "$OP_SERVICE_ACCOUNT_TOKEN" ]; then
echo "op: 1Password service account token file $token_file is empty" >&2
exit 78
fi
export OP_SERVICE_ACCOUNT_TOKEN
# `op run` (and `op plugin run`) hand their own environment, token included,
# to the command they start. Find the subcommand, skipping global flags that
# take a value, and stop at the first `--`.
sub1=""; sub2=""; skip=0; help=0
for a in "$@"; do
if [ "$skip" = 1 ]; then skip=0; continue; fi
case "$a" in
--) break ;;
-h|--help) help=1 ;;
--account|--config|--encoding|--format|--session) skip=1 ;;
-*) ;;
*) if [ -z "$sub1" ]; then sub1=$a; elif [ -z "$sub2" ]; then sub2=$a; fi ;;
esac
done
if [ "$sub1" = run ] || { [ "$sub1" = plugin ] && [ "$sub2" = run ]; }; then
# Rebuild the arguments with `env -u` spliced in after the first `--`, so
# the child starts without the token.
n=$#; i=0; spliced=0
while [ "$i" -lt "$n" ]; do
a=$1; shift
set -- "$@" "$a"
if [ "$spliced" = 0 ] && [ "$a" = "--" ]; then
set -- "$@" /usr/bin/env -u OP_SERVICE_ACCOUNT_TOKEN
spliced=1
fi
i=$((i + 1))
done
if [ "$spliced" = 0 ] && [ "$help" = 0 ]; then
echo "op: use 'op run [flags] -- <command>'; the shim needs the '--' to keep the service account token out of <command>" >&2
exit 64
fi
fi
exec "{{ multica_op_binary }}" "$@"
+4 -1
View File
@@ -25,7 +25,7 @@ only. Entry point: `playbooks/provision-wong-host.yml`.
| `/srv/obsidian/` | `0710 obsync` | `user:multica:--x` |
| `/srv/obsidian/vault/` | `0710 obsync` | `user:multica:--x`, so `multica` can traverse but not list |
| `vault/everpure/`, `vault/wip/` | `0700 obsync` | none |
| `vault/inbox/` | `0770 obsync` | `user:multica:rwx`, plus the same as a **default** entry |
| `vault/inbox/` | `0770 obsync` | `user:multica:rwx` and `user:obsync:rwx`, each also as a **default** entry |
On a directory with an ACL, the mode's group bits are the ACL mask. A
`chmod 0700` would therefore void every named entry, which is why the
@@ -38,6 +38,9 @@ only. Entry point: `playbooks/provision-wong-host.yml`.
- `multica` writes, reads and deletes in `inbox/`.
- A folder and file created by `obsync` under umask `0077` (the way Sync
creates them) stay readable and writable by `multica`.
- What `multica` creates stays readable and writable by `obsync`. Those
files are owned `multica:multica`, so without the named `user:obsync`
default entry Sync could not read them and would never upload them.
- `multica` is refused both listing the vault root and listing or writing
`everpure/` and `wip/`.
@@ -4,8 +4,13 @@
# <parent>/ obsync 0710 + user:<shared>:--x traverse, no listing
# <vault>/ obsync 0710 + user:<shared>:--x traverse, no listing
# <vault>/<private>/ obsync 0700 nobody else
# <vault>/<shared>/ obsync 0770 + user:<u>:rwx, and the same as a
# DEFAULT entry, so new children inherit
# <vault>/<shared>/ obsync 0770 + user:<u>:rwx and user:obsync:rwx,
# each also as a DEFAULT entry, so new
# children inherit both
#
# The user:obsync entry matters for content the SHARED user creates: that is
# owned <u>:<u>, so without a named entry obsync falls into "other" (---)
# and Sync can neither read nor upload it.
#
# The owning group is obsync's private group, so its bits grant nobody. On a
# directory that carries an ACL, the group bits of the mode ARE the ACL mask,
@@ -62,6 +67,18 @@
loop_control:
label: "{{ item.path }}"
# Each shared folder gets its user AND the service user, both as access and
# default entries.
- name: Layout | Build the shared-folder grants
ansible.builtin.set_fact:
_obh_grants: >-
{{ (obsidian_headless_shared_folders
+ (obsidian_headless_shared_folders
| map(attribute='path')
| map('community.general.dict_kv', 'path') | list
| map('combine', {'user': obsidian_headless_user}) | list))
| unique | list }}
- name: Layout | Grant shared folders (access and default entries)
ansible.posix.acl:
path: "{{ obsidian_headless_vault_path }}/{{ item.0.path }}"
@@ -70,6 +87,6 @@
permissions: rwx
default: "{{ item.1 }}"
state: present
loop: "{{ obsidian_headless_shared_folders | product([false, true]) | list }}"
loop: "{{ _obh_grants | product([false, true]) | list }}"
loop_control:
label: "{{ item.0.path }} -> {{ item.0.user }}{{ ' (default)' if item.1 else '' }}"
@@ -18,6 +18,61 @@
changed_when: false
check_mode: false
# The reverse direction: what the shared user writes, Sync must be able to
# read (to upload it) and rewrite (to merge remote edits into it).
- name: Verify | Content a shared user creates stays usable by the service (create)
ansible.builtin.command:
argv:
- /bin/sh
- -c
- 'd="$1/.obh-verify-rev"; mkdir -p "$d" && echo ok > "$d/note.md"'
- sh
- "{{ obsidian_headless_vault_path }}/{{ item.path }}"
become: true
become_user: "{{ item.user }}"
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.user }} -> {{ item.path }}"
changed_when: false
check_mode: false
- name: Verify | Content a shared user creates stays usable by the service (use)
ansible.builtin.command:
argv:
- /bin/sh
- -c
- 'd="$1/.obh-verify-rev"; grep -q ok "$d/note.md" && echo more >> "$d/note.md" && echo ok > "$d/reply.md"'
- sh
- "{{ obsidian_headless_vault_path }}/{{ item.path }}"
become: true
become_user: "{{ obsidian_headless_user }}"
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.path }}"
register: _obh_reverse
changed_when: false
failed_when: false
check_mode: false
- name: Verify | Remove the reverse probes
ansible.builtin.file:
path: "{{ obsidian_headless_vault_path }}/{{ item.path }}/.obh-verify-rev"
state: absent
loop: "{{ obsidian_headless_shared_folders }}"
loop_control:
label: "{{ item.path }}"
changed_when: false
- name: Verify | The service can read and rewrite shared users' content
ansible.builtin.assert:
that:
- _obh_reverse.results | rejectattr('rc', 'equalto', 0) | list | length == 0
fail_msg: >-
{{ obsidian_headless_user }} cannot read or write content a shared user
created; Sync would never upload it. Check the user:{{ obsidian_headless_user }}
default ACL entry on the shared folder.
quiet: true
# Simulates the service: obsync, umask 0077, creating a folder and a file the
# way Sync does. The shared user must still reach both, via the default ACL.
- name: Verify | Content the service creates in a shared folder inherits the grant (create)
-58
View File
@@ -1,58 +0,0 @@
services:
gitea:
image: docker.io/gitea/gitea:1.25.4
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=gitea_db
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${DATABASE_PASSWORD}
- GITEA__server__SSH_PORT=2221
- GITEA__server__ROOT_URL=https://gitea.mk-labs.cloud
- GITEA__server__DOMAIN=gitea.mk-labs.cloud
- GITEA__server__HTTP_PORT=3000
- GITEA__service__TRUSTED_PROXIES=10.1.71.35
networks:
- gitea_backend
ports:
- "3000:3000"
- "2221:22"
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
depends_on:
gitea_db:
condition: service_healthy
gitea_db:
image: docker.io/library/postgres:17.8
restart: unless-stopped
environment:
- TZ=America/Chicago
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
- POSTGRES_DB=gitea
networks:
- gitea_backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gitea"]
start_period: 30s
interval: 10s
timeout: 10s
retries: 5
volumes:
- gitea_db:/var/lib/postgresql/data
volumes:
gitea_data:
driver: local
gitea_db:
driver: local
networks:
gitea_backend:
driver: bridge
+3 -1
View File
@@ -1,13 +1,15 @@
http:
routers:
gitea:
rule: "Host(`gitea.mk-labs.cloud`)"
rule: "Host(`gitea.mk-labs.cloud`) || Host(`mad-tea-party.wdw-labs.cloud`)"
entryPoints:
- websecure
tls:
certResolver: cloudflare
domains:
- main: "gitea.mk-labs.cloud"
sans:
- "mad-tea-party.wdw-labs.cloud"
service: gitea
services: