{"id":36986195,"url":"https://github.com/citomni/infrastructure","last_synced_at":"2026-01-13T23:04:08.710Z","repository":{"id":317920414,"uuid":"1064379447","full_name":"citomni/infrastructure","owner":"citomni","description":"CitOmni Infrastructure provides lean, cross-mode services and building blocks designed to support both HTTP and CLI applications. It focuses on performance, simplicity, and predictable behavior, offering a common layer that applications can rely on without overhead or unnecessary abstractions.","archived":false,"fork":false,"pushed_at":"2025-11-01T21:37:06.000Z","size":307,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-01T23:26:23.299Z","etag":null,"topics":["captcha","citomni","citomni-framework","citomni-php","cli","deterministic","green-by-design","high-performance","http","infrastructure","low-overhead","php","php8","psr-4"],"latest_commit_sha":null,"homepage":"https://www.citomni.com/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/citomni.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-26T00:30:51.000Z","updated_at":"2025-11-01T21:37:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"4a019c2e-0fed-4ec7-aab2-ff02d3a56d9f","html_url":"https://github.com/citomni/infrastructure","commit_stats":null,"previous_names":["citomni/infrastructure"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/citomni/infrastructure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/citomni%2Finfrastructure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/citomni%2Finfrastructure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/citomni%2Finfrastructure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/citomni%2Finfrastructure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/citomni","download_url":"https://codeload.github.com/citomni/infrastructure/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/citomni%2Finfrastructure/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28405121,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T21:51:37.118Z","status":"ssl_error","status_checked_at":"2026-01-13T21:45:14.585Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["captcha","citomni","citomni-framework","citomni-php","cli","deterministic","green-by-design","high-performance","http","infrastructure","low-overhead","php","php8","psr-4"],"created_at":"2026-01-13T23:04:07.838Z","updated_at":"2026-01-13T23:04:08.705Z","avatar_url":"https://github.com/citomni.png","language":"PHP","readme":"# CitOmni Infrastructure\n\nLean, cross-mode infrastructure for CitOmni apps.\nPredictable service maps, deterministic config (last-wins), no magic.\nUltra-fast PHP 8.2+, side-effect free, designed for **HTTP *and* CLI** runtimes. ♻️\n\n---\n\n## Highlights\n\n* **Shared services** available in both HTTP \u0026 CLI (`db`, `log`, `txt`, `mailer`)\n* **Deterministic boot** -\u003e vendor baseline -\u003e providers -\u003e app (**last wins**)\n* **No scanning** -\u003e `$this-\u003eapp-\u003e{id}` resolves instantly (cacheable maps)\n* **Prod-friendly** -\u003e config/service maps can be precompiled by the App\n* **Infrastructure focus** -\u003e DB (LiteMySQLi), logging, mail (PHPMailer), text/i18n\n\n---\n\n## Requirements\n\n* PHP **8.2+**\n* Extensions:\n\n  * `ext-json` (standard)\n  * `ext-iconv` or `ext-mbstring` (mailer UTF-8 normalization; one of them is used)\n  * **`ext-gd`** (required for the optional `/captcha` route)\n  * Freetype (optional) enables TTF text in captcha (falls back to bitmap fonts if missing)\n* OPcache recommended in production\n\n---\n\n## Install\n\n```bash\ncomposer require citomni/infrastructure\ncomposer dump-autoload -o\n```\n\nEnsure your app is PSR-4 mapped:\n\n```json\n{\n\t\"autoload\": { \"psr-4\": { \"App\\\\\": \"src/\" } }\n}\n```\n\nEnable the provider in **`/config/providers.php`**:\n\n```php\n\u003c?php\nreturn [\n\t\\CitOmni\\Infrastructure\\Boot\\Services::class,\n];\n```\n\n---\n\n## Services (exported IDs)\n\nThis package contributes a **baseline service map** (app can override in `/config/services.php`):\n\n| id       | class                                   | purpose                           |\n| -------- | --------------------------------------- | --------------------------------- |\n| `db`     | `CitOmni\\Infrastructure\\Service\\Db`     | LiteMySQLi wrapper (lazy connect) |\n| `log`    | `CitOmni\\Infrastructure\\Service\\Log`    | LiteLog (JSONL etc., with rotate) |\n| `txt`    | `CitOmni\\Infrastructure\\Service\\Txt`    | Static text/i18n loader (LiteTxt) |\n| `mailer` | `CitOmni\\Infrastructure\\Service\\Mailer` | PHPMailer wrapper (+ logging)     |\n\n---\n\n## Constructor contract (all services)\n\n```php\n__construct(\\CitOmni\\Kernel\\App $app, array $options = [])\n```\n\n---\n\n**Usage:**\n\n```php\n// DB\n$id  = $this-\u003eapp-\u003edb-\u003einsert('crm_msg', ['msg_subject' =\u003e 'Hi']);\n$row = $this-\u003eapp-\u003edb-\u003efetchRow('SELECT * FROM crm_msg WHERE id=?', [$id]);\n\n// Logging\n$this-\u003eapp-\u003elog-\u003ewrite('orders.jsonl', 'order.create', ['id'=\u003e$id,'total'=\u003e$total]);\n\n// Text (i18n)\n$this-\u003eapp-\u003etxt-\u003eget('err_invalid_email', 'contact', 'citomni/infrastructure', 'Invalid.');\n\n// Mail\n$this-\u003eapp-\u003emailer\n\t-\u003eto('user@example.com')\n\t-\u003esubject('Welcome, {name}')\n\t-\u003etemplateVars(['name' =\u003e 'Sarah'])\n\t-\u003ebody('\u003cp\u003eHello {name}\u003c/p\u003e', true)\n\t-\u003esend();\n```\n\n---\n\n## Configuration (last wins)\n\nAt runtime the App builds config as:\n\n1. Vendor infrastructure baseline\n   `\\CitOmni\\Infrastructure\\Boot\\Services::CFG_HTTP` (and `CFG_CLI`)\n2. Provider CFGs (if any) listed in `/config/providers.php`\n3. App base cfg: `/config/citomni_http_cfg.php` or `/config/citomni_cli_cfg.php`\n4. App env overlay: `/config/citomni_{http|cli}_cfg.{env}.php` (optional)\n\n**Merge rules:** Associative arrays are deep-merged (**last wins**). Numeric lists are replaced by the last source.\n\n### Important keys used by this package\n\n```php\n'db' =\u003e [\n\t'host' =\u003e 'localhost',\n\t'user' =\u003e 'root',\n\t'pass' =\u003e '',\n\t'name' =\u003e 'citomni',\n\t'charset' =\u003e 'utf8mb4',\n],\n\n'log' =\u003e [\n\t'path'         =\u003e CITOMNI_APP_PATH . '/var/logs',\n\t'default_file' =\u003e 'citomni_app.log',\n\t'max_bytes'    =\u003e 2_000_000,\n\t'max_files'    =\u003e 10, // null = unlimited\n],\n\n'txt' =\u003e [\n\t'log' =\u003e [\n\t\t'file' =\u003e 'litetxt_errors.jsonl',\n\t\t'path' =\u003e CITOMNI_APP_PATH . '/var/logs',\n\t],\n],\n\n'mail' =\u003e [\n\t'from'      =\u003e ['email' =\u003e '', 'name' =\u003e ''],\n\t'reply_to'  =\u003e ['email' =\u003e '', 'name' =\u003e ''],\n\t'format'    =\u003e 'html',      // 'html' | 'text'\n\t'transport' =\u003e 'smtp',      // 'smtp' | 'mail' | 'sendmail' | 'qmail'\n\t// 'sendmail_path' =\u003e '/usr/sbin/sendmail',\n\t'smtp' =\u003e [\n\t\t'host'       =\u003e '',\n\t\t'port'       =\u003e 587,\n\t\t'encryption' =\u003e null,     // 'tls' | 'ssl' | null\n\t\t'auth'       =\u003e true,\n\t\t'username'   =\u003e '',\n\t\t'password'   =\u003e '',\n\t\t'auto_tls'   =\u003e true,\n\t\t'timeout'    =\u003e 15,\n\t\t'keepalive'  =\u003e false,\n\t],\n\t'logging' =\u003e [\n\t\t'log_success'     =\u003e false, // dev aid\n\t\t'debug_transcript' =\u003e false,\n\t\t'max_lines'        =\u003e 200,\n\t\t'include_bodies'   =\u003e false, // keep false in prod\n\t],\n],\n\n'security' =\u003e [\n\t'csrf_protection'      =\u003e true,\n\t'csrf_field_name'      =\u003e 'csrf_token',\n\t'captcha_protection'   =\u003e true,\n\t'honeypot_protection'  =\u003e true,\n\t'form_action_switching'=\u003e true,\n],\n\n'routes' =\u003e [\n\t'/kontakt.html' =\u003e [\n\t\t'controller'     =\u003e \\CitOmni\\Infrastructure\\Controller\\InfrastructureController::class,\n\t\t'action'         =\u003e 'contact',\n\t\t'methods'        =\u003e ['GET','POST'],\n\t\t'template_file'  =\u003e 'public/contact.html',\n\t\t'template_layer' =\u003e 'citomni/infrastructure',\n\t],\n\t'/captcha' =\u003e [\n\t\t'controller' =\u003e \\CitOmni\\Infrastructure\\Controller\\InfrastructureController::class,\n\t\t'action'     =\u003e 'captcha',\n\t\t'methods'    =\u003e ['GET'],\n\t],\n],\n```\n\n\u003e The HTTP router reads **routes as raw arrays** (`$this-\u003eapp-\u003ecfg-\u003eroutes[...]`).\n\n---\n\n## DB service (`db`)\n\nThin wrapper around **LiteMySQLi** with **lazy connection** and ergonomic `__call()` pass-through:\n\n```php\n$id = $this-\u003eapp-\u003edb-\u003einsert('crm_msg', ['msg_subject' =\u003e 'Hi']);\n$row = $this-\u003eapp-\u003edb-\u003efetchRow('SELECT * FROM crm_msg WHERE id=?', [$id]);\n```\n\nFor models, you can extend `CitOmni\\Infrastructure\\Model\\BaseModelLiteMySQLi` and access `$this-\u003edb`.\n\n---\n\n## Logging (`log`)\n\nBacked by **LiteLog**:\n\n```php\n$this-\u003eapp-\u003elog-\u003ewrite('order.jsonl', 'order.create', ['id'=\u003e$id,'total'=\u003e$total]);\n```\n\nRotation controlled by `log.max_bytes` and `log.max_files`. Directory defaults to `var/logs`.\n\n---\n\n## Text / i18n (`txt`)\n\nStatic text loader via **LiteTxt**, with layered paths:\n\n```php\n$this-\u003eapp-\u003etxt-\u003eget($key, $file, $layer='app', $default='', $vars=[]);\n```\n\n* `layer='app'` -\u003e `CITOMNI_APP_PATH/language/{lang}/{file}.php`\n* `layer='vendor/package'` -\u003e `CITOMNI_APP_PATH/vendor/{layer}/language/{lang}/{file}.php`\n* Language comes from `cfg['locale']['language']` (`'da'`, `'da_DK'`, etc.)\n* **Placeholders**: `%UPPER_CASE%` -\u003e replaced from `$vars` (e.g. `%APP_NAME%`)\n\nErrors go to `txt.log.file` (default `litetxt_errors.jsonl`).\n\n---\n\n## Mailer (`mailer`)\n\nPHPMailer wrapper with sensible defaults:\n\n```php\n$this-\u003eapp-\u003emailer\n\t-\u003efrom('system@example.com', 'CitOmni')\n\t-\u003eto(['john@example.com','jane@example.com'])\n\t-\u003esubject('Welcome, {name}')\n\t-\u003etemplateVars(['name' =\u003e 'Sarah'])\n\t-\u003ebody('\u003cp\u003eHello {name}\u003c/p\u003e', true)\n\t-\u003esend();\n```\n\n* **Transport**: `smtp`, `mail`, `sendmail`, `qmail` (from cfg)\n* **Default From/Reply-To** read from cfg\n* **Templating**: `{var}` placeholders (mailer-only) via `templateVars()`\n* Auto-generates `AltBody` from HTML if you don't set it\n* Logging:\n\n  * Success (dev-friendly): `mail_log.json` when `mail.logging.log_success=true`\n  * Errors: `mailer_errors.json` with optional SMTP transcript (`debug_transcript`)\n\n---\n\n## Contact form (optional)\n\nIf you keep the provided routes:\n\n* `GET|POST /kontakt.html` -\u003e validates, stores in DB (`crm_msg`), emails app recipient\n* `GET /captcha` -\u003e returns a PNG captcha using `ext-gd`\n  Fonts (optional) read from `vendor/citomni/infrastructure/assets/fonts/*.ttf`\n\n**Security interplay**: honors `security.csrf_protection`, `captcha_protection`, and `honeypot_protection`.\n\n**Recipient**: `cfg['identity']['email']` (fallback: `cfg['mail']['from']['email']`).\n\n\u003e If you plan to use the contact form routes, import the schema now (see Database schema below).\n\n---\n\n## Database schema\n\nThis package ships a ready-to-apply SQL schema for the contact form model:\n\n* File: `vendor/citomni/infrastructure/sql/crm_msg.sql`\n  Creates table **`crm_msg`** (InnoDB, `utf8mb4_unicode_ci`, PK `id` auto-increment). Works on MySQL 8+ / MariaDB 10.4+. \n\n### Option A — Manual import (recommended)\n\nUse your preferred tool:\n\n**MySQL CLI**\n\n```bash\nmysql -u \u003cuser\u003e -p \u003cdatabase\u003e \u003c vendor/citomni/infrastructure/sql/crm_msg.sql\n```\n\n**phpMyAdmin / Adminer**\n\n* Open your database\n* Import the file: `vendor/citomni/infrastructure/sql/crm_msg.sql`\n\n### Option B — Code-based, one-off installer (idempotent)\n\nIf you prefer to install via code, run once during setup/deploy:\n\n```php\n\u003c?php\ndeclare(strict_types=1);\n\nrequire __DIR__ . '/vendor/autoload.php';\n\ndefine('CITOMNI_ENVIRONMENT', 'cli');\ndefine('CITOMNI_APP_PATH', __DIR__);\n\n$app = new \\CitOmni\\Kernel\\App(__DIR__ . '/config', \\CitOmni\\Kernel\\Mode::CLI);\n$sql = (string)\\file_get_contents(__DIR__ . '/vendor/citomni/infrastructure/sql/crm_msg.sql');\n$app-\u003edb-\u003eexecute($sql);\necho \"crm_msg installed.\\n\";\n```\n\n\u003e Keep this script out of web-root; run it once, then delete it.\n\u003e The `Db` service must have `CREATE` privileges, otherwise use Option A.\n\n### Notes\n\n* The `CrmMessageModel` expects the table name **`crm_msg`** and the columns defined in the SQL file. \n* You can add indexes later to fit your reporting needs (e.g., `msg_added_dt`, `msg_from_email`).\n\n---\n\n## Why no auto-migrate?\n\nCitOmni packages are **side-effect free** by design. Vendor code should not create or alter your database automatically. That keeps the runtime **predictable**, **reviewable**, and **safe** across environments.\n\n**Why this policy exists**\n\n* **Determinism \u0026 reviewability** – DB changes live in your app/ops repos, not hidden in vendor code.\n* **Least privilege** – production credentials often lack `CREATE/ALTER`; installs shouldn’t assume elevated rights.\n* **Safer deploys** – no surprise schema writes that can fail under load, lock tables, or break blue/green rollouts.\n* **Compliance \u0026 audit** – schema changes pass through your change-management and CI/CD, with diffs and approvals.\n* **Multi-env parity** – staging/prod may be managed by DBAs; the package must work without mutating state.\n\n**What to do instead**\n\n* Use the provided SQL once (see **Database schema** above), or\n* Maintain **app-owned migrations** (idempotent SQL, `IF NOT EXISTS`, transactional where possible), executed by your deploy pipeline or a CLI command in your app.\n* Track schema with a simple `schema_version` table (or your existing migration tool).\n\nThis keeps the infrastructure package **stateless**, while your application controls **when and how** the database evolves.\n\n---\n\n## Performance tips\n\n* Composer:\n\n  ```json\n  { \"config\": { \"optimize-autoloader\": true, \"classmap-authoritative\": true, \"apcu-autoloader\": true } }\n  ```\n\n  Then: `composer dump-autoload -o`\n\n* OPcache (prod):\n\n  ```\n  opcache.enable=1\n  opcache.validate_timestamps=0\n  opcache.revalidate_path=0\n  opcache.save_comments=0\n  realpath_cache_size=4096k\n  realpath_cache_ttl=600\n  ```\n\n---\n\n## Contributing\n\n* PHP 8.2+, PSR-4, **tabs**, K\u0026R braces\n* Keep vendor files side-effect free (OPcache-friendly)\n* Don't swallow exceptions in core; let the global error handler log\n\n---\n\n## Coding \u0026 Documentation Conventions\n\nAll CitOmni projects follow the shared conventions documented here:  \n[CitOmni Coding \u0026 Documentation Conventions](https://github.com/citomni/docs/blob/main/contribute/CONVENTIONS.md)\n\n---\n\n## License\n\n**CitOmni Infrastructure** is open-source under the **MIT License**.  \nSee: [LICENSE](LICENSE).\n\n**Trademark notice:** \"CitOmni\" and the CitOmni logo are trademarks of **Lars Grove Mortensen**.  \nYou may not use the CitOmni name or logo to imply endorsement or affiliation without prior written permission.\n\n---\n\n## Trademarks\n\n\"CitOmni\" and the CitOmni logo are trademarks of **Lars Grove Mortensen**.  \nYou may make factual references to \"CitOmni\", but do not modify the marks, create confusingly similar logos,  \nor imply sponsorship, endorsement, or affiliation without prior written permission.  \nDo not register or use \"citomni\" (or confusingly similar terms) in company names, domains, social handles, or top-level vendor/package names.  \nFor details, see the project's [NOTICE](NOTICE).\n\n---\n\n## Author\n\nDeveloped by **Lars Grove Mortensen** © 2012-present\nContributions and pull requests are welcome!\n\n---\n\nBuilt with ❤️ on the CitOmni philosophy: **low overhead**, **high performance**, and **ready for anything**.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcitomni%2Finfrastructure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcitomni%2Finfrastructure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcitomni%2Finfrastructure/lists"}