--- name: synapse_msc3861_enable kind: function lang: go domain: infra version: "0.1.0" purity: impure signature: "func SynapseMsc3861Enable(cfg SynapseMsc3861Config) (SynapseMsc3861Result, error)" description: "Edita homeserver.yaml de Synapse activando el bloque matrix_authentication_service (MSC3861/MAS), asegura experimental_features.msc3861.enabled=true y password_config.enabled=false. Preserva comentarios con yaml.v3 Node API. Hace backup automático previo y devuelve diff unified." tags: [matrix, mas, synapse, msc3861, migration, mas-migration, infra, yaml, matrix-mas] params: - name: HomeserverYamlPath desc: "Ruta absoluta al homeserver.yaml en disco local (normalmente copiado del VPS con scp antes de llamar esta función)" - name: MasEndpoint desc: "URL interna del servicio MAS (ej. http://mas:8080/). Debe empezar con http:// o https://" - name: MasSecret desc: "Shared secret hex de exactamente 64 caracteres (32 bytes) que debe coincidir con mas/config.yaml::matrix.secret" - name: BackupDir desc: "Directorio donde guardar el backup del archivo original (se crea con mkdir -p si no existe). Ej: /tmp/synapse_backups" - name: DryRun desc: "Si true, sólo computa el diff sin escribir archivos ni crear backup" output: "SynapseMsc3861Result con BackupPath (vacío si DryRun), LinesAdded, LinesRemoved y Diff (unified diff string)" uses_functions: [] uses_types: ["error_go_core"] returns: [] returns_optional: false error_type: "error_go_core" imports: ["gopkg.in/yaml.v3"] tested: true tests: - "commented mas block becomes active" - "already active mas block gets updated values" - "no mas block inserts block at end" - "dry run does not write file" test_file_path: "functions/infra/synapse_msc3861_enable_test.go" file_path: "functions/infra/synapse_msc3861_enable.go" --- ## Ejemplo ```go cfg := SynapseMsc3861Config{ HomeserverYamlPath: "/tmp/synapse_data/homeserver.yaml", MasEndpoint: "http://mas:8080/", MasSecret: "5506f8b2f3fbb50413244e7197599e26477b179ec4917787f352d090fb7c7eb2", BackupDir: "/tmp/synapse_backups", DryRun: true, } res, err := SynapseMsc3861Enable(cfg) if err != nil { log.Fatal(err) } fmt.Printf("Diff:\n%s\n", res.Diff) fmt.Printf("Lines added: %d, removed: %d\n", res.LinesAdded, res.LinesRemoved) // Para aplicar los cambios: DryRun: false // res.BackupPath contiene la ruta del backup creado. ``` ## Cuando usarla Paso 3 de la migración 0162 (Synapse → MAS auth provider): después de copiar `homeserver.yaml` del VPS a disco local con `scp`, antes de copiarlo de vuelta con `scp` y hacer `systemctl restart matrix-synapse`. Usar `DryRun: true` primero para revisar el diff antes de escribir. ## Gotchas - **yaml.v3 Node API obligatorio**: el YAML de Synapse contiene comentarios críticos de configuración. Usar `yaml.Unmarshal` plano los elimina. Esta función usa la API de nodos para la sección `experimental_features` y edición line-level para los bloques `matrix_authentication_service` y `password_config`. - **MasSecret debe ser exacto**: debe coincidir byte a byte con `mas/config.yaml::matrix.secret`. Un carácter diferente hace que Synapse rechace todas las peticiones MAS con 401. - **Nunca editar in-place en el VPS activo**: editar el archivo mientras Synapse lo lee puede producir YAML corrupto en memoria. El flujo correcto es: `scp vps:/etc/matrix-synapse/homeserver.yaml /tmp/` → `SynapseMsc3861Enable(DryRun: false)` → `scp /tmp/homeserver.yaml vps:/etc/matrix-synapse/` → `systemctl restart matrix-synapse`. - **MasSecret formato**: exactamente 64 caracteres hexadecimales en minúsculas (32 bytes). La validación rechaza mayúsculas y longitudes incorrectas. - **Idempotencia**: aplicar la función dos veces sobre el mismo archivo produce el mismo resultado final (el segundo pase actualiza valores ya existentes).