From c9d7127ee25e315fc3b192b2ab14224f11b1927f Mon Sep 17 00:00:00 2001 From: tzzrgit Date: Sun, 21 Dec 2025 18:11:43 +0100 Subject: [PATCH] Add documentation and SPEC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update README with full project documentation - Add docs/SPEC.md with complete technical specification πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- README.md | 119 ++++++++++- docs/SPEC.md | 565 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 674 insertions(+), 10 deletions(-) create mode 100644 docs/SPEC.md diff --git a/README.md b/README.md index 3c2783f..8cee93e 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,115 @@ -# packet +# Packet -A new Flutter project. +App mΓ³vil multiplataforma (iOS/Android) para capturar contenido multimedia, etiquetarlo con hashes y enviarlo a backends configurables. -## Getting Started +## Principios -This project is a starting point for a Flutter application. +| Principio | DescripciΓ³n | +|-----------|-------------| +| Zero-retention | Los archivos nunca se almacenan en disco, solo en RAM hasta envΓ­o | +| Hash-first | Todo contenedor tiene hash SHA-256 ΓΊnico generado localmente | +| Contenedor cerrado | Una vez montado, no se inspecciona. Solo enviar o desmontar | +| App tonta | No valida coherencia de etiquetas, solo formato. El backend decide | +| Offline-capable | Captura sin conexiΓ³n, cola de reintentos cuando hay red | -A few resources to get you started if this is your first Flutter project: +## Stack TecnolΓ³gico -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) +| Componente | TecnologΓ­a | +|------------|------------| +| Framework | Flutter 3.x | +| Lenguaje | Dart | +| Estado | flutter_bloc | +| DB Local | sqflite | +| HTTP | dio | +| Crypto | crypto (SHA-256) | +| Media | image_picker, record | +| Location | geolocator | +| Permisos | permission_handler | -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +## Pantallas + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ [Mi DECK β–Ό] [CORP] [+Ext] β”‚ ← Destinos (superior) +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ CONTENIDO PANTALLA β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ πŸ“· 🏷️ πŸ“¦ ⏳ βš™οΈ β”‚ ← NavegaciΓ³n (inferior) +β”‚ Captura Tags Packs Pend. Config β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Captura +Montar contenedor con contenido multimedia: tΓ­tulo, descripciΓ³n, archivos, GPS, etiquetas. + +### Etiquetas +Seleccionar etiquetas de bibliotecas HST/DECK y aΓ±adir hashes externos. + +### Packs +Conjuntos predefinidos de etiquetas para aplicar con 1 tap. + +### Pendientes +Cola de contenedores fallidos (mΓ‘x 20). App se bloquea si se llena. + +### Config +URLs, llaves de autenticaciΓ³n y bibliotecas de iconos. + +## Estructura del Proyecto + +``` +lib/ +β”œβ”€β”€ core/ +β”‚ β”œβ”€β”€ constants/ # Constantes de la app +β”‚ β”œβ”€β”€ errors/ # Excepciones personalizadas +β”‚ β”œβ”€β”€ utils/ # Utilidades (hash, retry) +β”‚ └── theme/ # Tema de la app +β”œβ”€β”€ data/ +β”‚ β”œβ”€β”€ datasources/ # APIs y BD local +β”‚ └── repositories/ # Repositorios +β”œβ”€β”€ domain/ +β”‚ └── entities/ # Modelos de dominio +└── presentation/ + β”œβ”€β”€ bloc/ # Cubits de estado + β”œβ”€β”€ pages/ # Pantallas + └── widgets/ # Widgets reutilizables +``` + +## Build + +```bash +# Instalar dependencias +flutter pub get + +# Build APK +flutter build apk --release + +# Build iOS (requiere Xcode) +flutter build ios --release +``` + +## APIs + +### Backend (envΓ­o) +```http +POST {destino.url}/ingest +X-Auth-Key: {destino.hash} +Content-Type: application/json +``` + +### Bibliotecas (etiquetas) +```http +GET {biblioteca.url}/api/tags +``` + +## LΓ³gica de Reintentos + +20 intentos en 72 horas con backoff exponencial (1min β†’ 8h). + +## Referencias + +- Biblioteca HST: https://tzrtech.org +- EspecificaciΓ³n completa: [docs/SPEC.md](docs/SPEC.md) + +--- + +v1.0.0 diff --git a/docs/SPEC.md b/docs/SPEC.md new file mode 100644 index 0000000..6492b5b --- /dev/null +++ b/docs/SPEC.md @@ -0,0 +1,565 @@ +# Packet - EspecificaciΓ³n TΓ©cnica Completa + +## 1. VisiΓ³n General + +Packet permite: +- Capturar contenido multimedia (fotos, audio, video, archivos) +- Etiquetarlo con referencias de bibliotecas externas +- Empaquetarlo en contenedores con hash ΓΊnico +- Enviarlo a backends configurables + +### Principios + +| Principio | DescripciΓ³n | +|-----------|-------------| +| Zero-retention | Los archivos nunca se almacenan en disco, solo en RAM hasta envΓ­o | +| Hash-first | Todo contenedor tiene hash ΓΊnico generado localmente | +| Contenedor cerrado | Una vez montado, no se inspecciona. Solo enviar o desmontar | +| App tonta | No valida coherencia de etiquetas, solo formato. El backend decide | +| Offline-capable | Captura sin conexiΓ³n, cola de reintentos cuando hay red | + +--- + +## 2. Stack TecnolΓ³gico + +| Componente | TecnologΓ­a | +|------------|------------| +| Framework | Flutter 3.x | +| Lenguaje | Dart | +| Estado | flutter_bloc | +| DB Local | sqflite | +| HTTP | dio | +| Crypto | crypto (SHA-256) | +| Media | image_picker, record | +| Location | geolocator | +| Permisos | permission_handler | + +--- + +## 3. Estructura de Carpetas + +``` +packet/ +β”œβ”€β”€ android/ +β”œβ”€β”€ ios/ +β”œβ”€β”€ lib/ +β”‚ β”œβ”€β”€ core/ +β”‚ β”‚ β”œβ”€β”€ constants/ +β”‚ β”‚ β”‚ └── app_constants.dart +β”‚ β”‚ β”œβ”€β”€ errors/ +β”‚ β”‚ β”‚ └── exceptions.dart +β”‚ β”‚ β”œβ”€β”€ utils/ +β”‚ β”‚ β”‚ β”œβ”€β”€ hash_utils.dart +β”‚ β”‚ β”‚ └── retry_utils.dart +β”‚ β”‚ └── theme/ +β”‚ β”‚ └── app_theme.dart +β”‚ β”‚ +β”‚ β”œβ”€β”€ data/ +β”‚ β”‚ β”œβ”€β”€ datasources/ +β”‚ β”‚ β”‚ β”œβ”€β”€ local_database.dart +β”‚ β”‚ β”‚ β”œβ”€β”€ backend_api.dart +β”‚ β”‚ β”‚ └── biblioteca_api.dart +β”‚ β”‚ β”œβ”€β”€ models/ +β”‚ β”‚ β”‚ β”œβ”€β”€ destino_model.dart +β”‚ β”‚ β”‚ β”œβ”€β”€ contenedor_model.dart +β”‚ β”‚ β”‚ β”œβ”€β”€ etiqueta_model.dart +β”‚ β”‚ β”‚ └── pack_model.dart +β”‚ β”‚ └── repositories/ +β”‚ β”‚ β”œβ”€β”€ contenedor_repository.dart +β”‚ β”‚ β”œβ”€β”€ etiqueta_repository.dart +β”‚ β”‚ └── config_repository.dart +β”‚ β”‚ +β”‚ β”œβ”€β”€ domain/ +β”‚ β”‚ └── entities/ +β”‚ β”‚ β”œβ”€β”€ contenedor.dart +β”‚ β”‚ β”œβ”€β”€ etiqueta.dart +β”‚ β”‚ β”œβ”€β”€ pack.dart +β”‚ β”‚ β”œβ”€β”€ destino.dart +β”‚ β”‚ └── archivo_adjunto.dart +β”‚ β”‚ +β”‚ β”œβ”€β”€ presentation/ +β”‚ β”‚ β”œβ”€β”€ bloc/ +β”‚ β”‚ β”‚ β”œβ”€β”€ captura/ +β”‚ β”‚ β”‚ β”œβ”€β”€ etiquetas/ +β”‚ β”‚ β”‚ β”œβ”€β”€ packs/ +β”‚ β”‚ β”‚ β”œβ”€β”€ pendientes/ +β”‚ β”‚ β”‚ └── config/ +β”‚ β”‚ β”œβ”€β”€ pages/ +β”‚ β”‚ β”‚ β”œβ”€β”€ captura_page.dart +β”‚ β”‚ β”‚ β”œβ”€β”€ etiquetas_page.dart +β”‚ β”‚ β”‚ β”œβ”€β”€ packs_page.dart +β”‚ β”‚ β”‚ β”œβ”€β”€ pendientes_page.dart +β”‚ β”‚ β”‚ └── config_page.dart +β”‚ β”‚ β”œβ”€β”€ widgets/ +β”‚ β”‚ β”‚ β”œβ”€β”€ audio_recorder.dart +β”‚ β”‚ β”‚ β”œβ”€β”€ destino_selector.dart +β”‚ β”‚ β”‚ β”œβ”€β”€ etiqueta_grid.dart +β”‚ β”‚ β”‚ └── contenedor_card.dart +β”‚ β”‚ └── app.dart +β”‚ β”‚ +β”‚ └── main.dart +β”‚ +β”œβ”€β”€ test/ +β”œβ”€β”€ pubspec.yaml +└── README.md +``` + +--- + +## 4. Pantallas + +### NavegaciΓ³n + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ [Mi DECK β–Ό] [CORP] [+Ext] β”‚ ← Destinos (superior) +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ CONTENIDO PANTALLA β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ πŸ“· 🏷️ πŸ“¦ ⏳ βš™οΈ β”‚ ← NavegaciΓ³n (inferior) +β”‚ Captura Tags Packs Pend. Config β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### 4.1 Captura + +Montar contenedor con contenido multimedia. + +Campos: +- TΓ­tulo (string, opcional) +- DescripciΓ³n (string, opcional, se procesa en backend) +- Identificador (string 64 chars, auto-generado SHA-256) +- Archivos (array, opcional) +- GPS (lat/long, opcional) +- Etiquetas (array de hashes, seleccionadas en pestaΓ±a Etiquetas) + +Botones de captura: +- πŸ“· Foto (cΓ‘mara) +- 🎀 Audio (con contador de tiempo) +- πŸŽ₯ Video +- πŸ“Ž Archivo +- πŸ“ GPS + +El hash se genera al abrir la app o tras cada envΓ­o. Se puede enviar contenedor vacΓ­o (solo hash). Botones de copiar y regenerar hash visibles. + +### 4.2 Etiquetas + +Seleccionar etiquetas de bibliotecas y aΓ±adir hashes externos. + +Secciones: +1. Grid HST - Iconos de biblioteca HST (se oscurecen al seleccionar) +2. Grid DECK - Iconos de biblioteca DECK +3. Campo hash - Pegar URL o hash externo +4. Seleccionadas - Lista de etiquetas actuales con botΓ³n eliminar + +ValidaciΓ³n de hash externo: +1. Usuario pega texto +2. App busca patrΓ³n: [a-f0-9]{64} +3. Por cada match β†’ check verde inmediato +4. Background: consulta /api/tags para resolver nombre +5. Si encuentra β†’ muestra nombre +6. Si no β†’ muestra hash truncado + +### 4.3 Packs + +Conjuntos predefinidos de etiquetas creados localmente. + +- 1 tap β†’ aplica todas las etiquetas del pack +- CreaciΓ³n: nombre + etiquetas actualmente seleccionadas +- Almacenados en SQLite local + +### 4.4 Pendientes + +Cola de contenedores fallidos. LΓ­mite: 20 contenedores mΓ‘ximo. App se bloquea si se llena. + +Por cada contenedor muestra: +- Hash (truncado) +- TΓ­tulo (o "sin tΓ­tulo") +- NΓΊmero de archivos +- Intentos realizados (mΓ‘x 20) +- Último intento (hora) +- Estado (error / reintentando) + +Acciones: +- Reintentar β†’ Intenta enviar de nuevo +- Recuperar β†’ Extrae archivos al almacenamiento del dispositivo y elimina contenedor + +### 4.5 Config + +URLs, llaves y bibliotecas. + +Por cada destino: +- Nombre (identificador visual) +- URL Backend (endpoint de envΓ­o) +- Llave (hash de 64 chars para autenticaciΓ³n) + +Bibliotecas de iconos: +- URL (dominio) +- Endpoint (ruta del Γ­ndice, ej: /api/tags) +- Nombre (identificador visual) + +--- + +## 5. Modelos de Datos + +### Contenedor + +```dart +class Contenedor { + String hash; // 64 chars, generado localmente + String? titulo; + String? descripcion; + List archivos; + GpsLocation? gps; + List etiquetas; // Lista de hashes + DateTime createdAt; +} +``` + +### Etiqueta + +```dart +class Etiqueta { + String hMaestro; // Hash ΓΊnico (64 chars) + String hGlobal; // biblioteca + etiqueta (128 chars) + String? mrf; // Media reference para imagen + String? ref; // CΓ³digo corto + String? nombreEs; + String? nombreEn; + String grupo; + bool activo; +} +``` + +### Destino + +```dart +class Destino { + int id; + String nombre; + String url; + String hash; // Llave de autenticaciΓ³n + bool activo; +} +``` + +### Pack + +```dart +class Pack { + int id; + String nombre; + String icono; + List tags; // Lista de hashes +} +``` + +### ArchivoAdjunto + +```dart +class ArchivoAdjunto { + String nombre; + String mimeType; + Uint8List bytes; // En memoria, nunca en disco + FileType tipo; // image, audio, video, document + String? hash; +} +``` + +--- + +## 6. Base de Datos SQLite + +```sql +CREATE TABLE registro ( + hash TEXT PRIMARY KEY, + titulo TEXT, + hora_envio TEXT NOT NULL, + primera_conf TEXT, + ultima_conf TEXT, + destino_id INTEGER +); + +CREATE TABLE destinos ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + nombre TEXT NOT NULL, + url TEXT NOT NULL, + hash TEXT NOT NULL, + activo INTEGER DEFAULT 1 +); + +CREATE TABLE bibliotecas ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + nombre TEXT NOT NULL, + url TEXT NOT NULL, + endpoint TEXT NOT NULL, + h_biblioteca TEXT +); + +CREATE TABLE etiquetas_cache ( + h_maestro TEXT PRIMARY KEY, + h_global TEXT, + mrf TEXT, + ref TEXT, + nombre_es TEXT, + nombre_en TEXT, + grupo TEXT, + biblioteca_id INTEGER +); + +CREATE TABLE packs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + nombre TEXT NOT NULL, + icono TEXT DEFAULT πŸ“¦, + tags TEXT NOT NULL +); + +CREATE TABLE pendientes ( + hash TEXT PRIMARY KEY, + titulo TEXT, + contenido BLOB NOT NULL, + intentos INTEGER DEFAULT 0, + ultimo_intento TEXT, + proximo_intento TEXT, + destino_id INTEGER +); + +CREATE TABLE app_state ( + key TEXT PRIMARY KEY, + value TEXT +); +``` + +--- + +## 7. APIs Externas + +### Backend (envΓ­o) + +```http +POST {destino.url}/ingest +X-Auth-Key: {destino.hash} +Content-Type: application/json + +{ + "hash": "fc2fae65158ca7c1880dd3ff9b8ed9081107b50f9cdce08df30234fe166777ce", + "titulo": "ferr-23", + "descripcion": "Compra en ferreterΓ­a", + "etiquetas": ["a1b2c3d4...fc2fae65...", "a1b2c3d4...b2c3d4e5..."], + "gps": {"lat": 43.3623, "long": -8.4115}, + "archivos": [{"nombre": "foto.jpg", "tipo": "image/jpeg", "contenido": "base64..."}] +} + +Response 200: { "ok": true, "received_at": "2024-12-20T10:00:00Z" } +Response 409: { "error": "hash_exists" } +``` + +### EnvΓ­o por chunks (archivos grandes) + +```http +POST {destino.url}/upload/init +{ "hash": "...", "total_chunks": 50, "file_name": "video.mp4" } +β†’ { "upload_id": "..." } + +POST {destino.url}/upload/chunk/{upload_id}/{chunk_number} +Body: bytes (512KB - 1MB por chunk) +β†’ { "ok": true } + +POST {destino.url}/upload/complete/{upload_id} +β†’ { "ok": true, "file_hash": "..." } +``` + +### Bibliotecas (Γ­ndice de etiquetas) + +```http +GET {biblioteca.url}/api/tags + +Response: +{ + "count": 973, + "biblioteca": { + "h_biblioteca": "b7149f9e2106c566032aeb29a26e4c6cdd5f5c16b4421025c58166ee345740d1", + "nombre": "HST", + "publica": true + }, + "results": [ + { + "h_maestro": "fc2fae65...77ce", + "h_global": "b7149f9e...fc2fae65...77ce", + "mrf": "502dc114...", + "ref": "inv", + "nombre_es": "factura", + "nombre_en": "invoice", + "grupo": "hst", + "imagen_url": "https://tzrtech.org/502dc...png", + "activo": true + } + ] +} +``` + +ImΓ‘genes: GET {biblioteca.url}/{mrf}.png + +--- + +## 8. Formato de Referencias + +Hash simple (etiqueta): 64 caracteres hex +``` +fc2fae65158ca7c1880dd3ff9b8ed9081107b50f9cdce08df30234fe166777ce +``` + +Referencia global (biblioteca + etiqueta): 128 caracteres +``` +[h_biblioteca 64 chars][h_etiqueta 64 chars] +``` + +Cadena jerΓ‘rquica: mΓΊltiplo de 64, sin separador +``` +[biblioteca][nivel1][nivel2][nivel3]... +``` + +MΓΊltiples cadenas: separadas por \\n + +--- + +## 9. LΓ³gica de Reintentos + +20 intentos en 72 horas con backoff exponencial: + +| Intento | Espera | Acumulado | +|---------|--------|-----------| +| 1 | 1 min | 0 | +| 2 | 2 min | 1 min | +| 3 | 5 min | 3 min | +| 4 | 10 min | 8 min | +| 5 | 20 min | 18 min | +| 6 | 30 min | 38 min | +| 7 | 1 h | 1 h | +| 8 | 2 h | 2 h | +| 9 | 3 h | 4 h | +| 10 | 4 h | 7 h | +| 11 | 5 h | 11 h | +| 12 | 6 h | 16 h | +| 13 | 6 h | 22 h | +| 14 | 6 h | 28 h | +| 15 | 8 h | 34 h | +| 16 | 8 h | 42 h | +| 17 | 8 h | 50 h | +| 18 | 8 h | 58 h | +| 19 | 6 h | 66 h | +| 20 | STOP | 72 h | + +--- + +## 10. Persistencia de Estado + +La app restaura al abrir exactamente como se cerrΓ³: +- Destino seleccionado +- Pantalla activa +- Contenedor en progreso +- Etiquetas seleccionadas +- Cola de pendientes +- ConfiguraciΓ³n completa + +--- + +## 11. Permisos + +Android: +```xml + + + + + +``` + +iOS: +```xml +NSCameraUsageDescription +Para capturar fotos y videos +NSMicrophoneUsageDescription +Para grabar notas de voz +NSLocationWhenInUseUsageDescription +Para aΓ±adir ubicaciΓ³n a los contenedores +``` + +--- + +## 12. CI/CD + +```yaml +name: Build + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + - run: flutter pub get + - run: flutter test + + build-android: + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + - run: flutter build apk --release + + build-ios: + needs: test + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + - run: flutter build ios --release --no-codesign +``` + +--- + +## 13. Roadmap + +### v1.0 (MVP) +- 5 pantallas bΓ‘sicas +- Captura foto/audio/video +- EnvΓ­o a backend ΓΊnico +- Cola de pendientes +- Packs locales + +### v1.1 +- MΓΊltiples destinos +- EnvΓ­o por chunks +- Cache de bibliotecas + +### v1.2 +- Notificaciones push +- Widgets de acceso rΓ‘pido + +### v2.0 +- Credenciales para bibliotecas privadas +- Cadenas jerΓ‘rquicas visuales +- SincronizaciΓ³n entre dispositivos + +--- + +## 14. Referencias + +- Biblioteca HST: https://tzrtech.org +- API HST: https://tzrtech.org/api/tags +- h_biblioteca HST: b7149f9e2106c566032aeb29a26e4c6cdd5f5c16b4421025c58166ee345740d1 + +--- + +VersiΓ³n: 1.0 +Fecha: 2025-12-20