---
name: browser_profile_register
kind: function
lang: py
domain: browser
version: "1.0.0"
purity: impure
signature: "def browser_profile_register(profile_dir: str, label: str = '', persona: str = '', purpose: str = '', note_path: str = '', tags: list | None = None, notes: str = '', user_data_dir: str = '', status: str = 'active', accounts: list | None = None, base_url: str = 'http://127.0.0.1:8771') -> dict"
description: "Registra o actualiza un perfil de Chromium (y opcionalmente sus cuentas) en el catalogo del service osint_db (FastAPI + DuckDB single-writer) usado para investigaciones multicuenta OSINT. En una sola llamada hace POST /api/browser-profile con la metadata del perfil (upsert idempotente sobre profile_dir) y un POST /api/browser-profile/account por cada cuenta de la lista accounts. El service responde SIEMPRE HTTP 200 con body {status:ok|error}, se parsea el body. Impura (red). No lanza: devuelve dict de estado. secret_ref de cada cuenta es una REFERENCIA al secreto (ej. 'pass show osint/p1/gmail'), nunca el password en claro."
tags: [browser-profiles, osint, chromium, profile, multicuenta]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_py_core"
imports: []
tested: true
tests: ["test_golden_registra_perfil_con_dos_cuentas", "test_edge_cuenta_invalida_se_reporta_y_no_se_envia", "test_error_post_perfil_falla_devuelve_status_error"]
test_file_path: "python/functions/browser/browser_profile_register_test.py"
file_path: "python/functions/browser/browser_profile_register.py"
params:
  - name: profile_dir
    desc: "Nombre del directorio real del perfil Chromium (ej. 'Profile 1', 'Default', 'osint_01'). Es la PK; el upsert es idempotente sobre el."
  - name: label
    desc: "Etiqueta humana del perfil (ej. 'Persona Maria - OSINT'). '' para omitir."
  - name: persona
    desc: "Identidad/alias ficticio asociado al perfil (sock puppet). '' para omitir."
  - name: purpose
    desc: "Proposito de la investigacion (ej. 'rastreo cuentas falsas'). '' para omitir."
  - name: note_path
    desc: "Ruta (rel al vault OSINT) de la nota ligada al perfil. '' para omitir."
  - name: tags
    desc: "Lista de strings de etiquetas del perfil (ej. ['osint','sock-puppet']). None -> []."
  - name: notes
    desc: "Notas libres sobre el perfil. '' para omitir."
  - name: user_data_dir
    desc: "user-data-dir de Chromium si NO es el default del wrapper chromium-cdp. '' -> el perfil hereda el default al abrirlo con browser_profile_open."
  - name: status
    desc: "Estado del perfil (active|archived|burned...). Default 'active'."
  - name: accounts
    desc: "Lista de dicts de cuentas a registrar: {service, identity, secret_ref?, role?, status?, notes?}. None -> sin cuentas. service ej. 'gmail', identity ej. 'x@y.com' o '@handle'. secret_ref es REFERENCIA al secreto, NUNCA el password."
  - name: base_url
    desc: "Base del service osint_db. Default http://127.0.0.1:8771."
output: "dict de estado. Caso ok: {status:'ok', profile_dir, accounts (int: cuentas registradas con exito), account_errors (list: errores por cuenta invalida o rechazada, vacia si todo OK)}. Caso error (fallo del POST del perfil): {status:'error', error: str}."
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from browser.browser_profile_register import browser_profile_register

res = browser_profile_register(
    "Profile 1",
    label="Persona Maria - OSINT",
    persona="maria_ficticia",
    purpose="rastreo cuentas falsas",
    tags=["osint", "sock-puppet"],
    accounts=[
        {"service": "gmail", "identity": "maria@example.com",
         "secret_ref": "pass show osint/p1/gmail"},
        {"service": "x", "identity": "@maria_fake", "role": "primary"},
    ],
)
print(res["status"])    # "ok" si el service esta vivo
print(res["accounts"])  # 2 (cuentas registradas)
```

## Cuando usarla

Cuando crees un perfil nuevo de Chromium para una investigacion multicuenta OSINT y
quieras dejarlo catalogado (con su persona, proposito y cuentas) en el service osint_db.
Llamala tambien para ACTUALIZAR un perfil existente: el upsert es idempotente sobre
`profile_dir`, asi que reejecutarla con mas cuentas o metadata nueva no duplica nada.
Es el punto de entrada del grupo `browser-profiles`; luego se lista con
`browser_profile_list`, se inspecciona con `browser_profile_show` y se abre con
`browser_profile_open`.

## Gotchas

- **Impura**: hace red (HTTP POST al service). El service `osint_db` debe estar vivo en
  `http://127.0.0.1:8771`. Si esta caido, devuelve `{status:'error', error:'... inaccesible'}`
  sin lanzar.
- **El codigo HTTP NO indica exito**: el service responde SIEMPRE HTTP 200 con body
  `{status:ok|error}`. La funcion parsea el body, no el codigo HTTP.
- **secret_ref NUNCA es el password**: es una REFERENCIA al secreto (ej.
  `"pass show osint/p1/gmail"`). No metas credenciales en claro — se resuelven con `pass`
  en el momento de usarlas.
- **Idempotente**: reejecutar con el mismo `profile_dir` actualiza (upsert), no duplica.
  Lo mismo para cada cuenta (PK `<profile_dir>:<service>:<identity>`).
- **Errores parciales de cuentas**: si el perfil se registra pero una cuenta falla (o le
  falta `service`/`identity`), el `status` global sigue siendo `"ok"` y el detalle del
  fallo va en `account_errors`. Solo `status:'error'` si falla el POST del PERFIL.
- **Single-writer DuckDB**: la DB la abre el service. NUNCA abrir `osint.duckdb` en
  paralelo; todo pasa por HTTP.

## Notas

Usa el helper compartido `python/functions/browser/_osint_db_client.py` (modulo privado
no indexado) para el POST sobre `urllib.request` de stdlib (sin `requests`). Timeout HTTP
de 10s por request.
