{"configuration":{},"description":"C4 diagrams","documentation":{"decisions":[{"content":"# ADR-001: Identity and Access Management Platform Selection\n\n## Status\nAccepted\nDate: 2024-12-11\n\n## Context\nOur application requires a Identity and Access Management (IAM) solution that can:\n- Handle authentication and authorization\n- Manage user identities and sessions\n- Provide MFA capabilities\n- Scale with our growth\n- Offer flexibility in deployment options\n\n## Decision\nWe decided to not go with Keycloak, because we don't want to manage a heavy monolithic solution and we want to check the lightweight modern solution. We will proceed with the ORY Stack due to its alignment with our specific requirements:\n\n### Key Requirements:\n1. User Management (via Kratos)\n   - Admin-managed user accounts only (self-registration disabled)\n   - Password management and reset\n   - 2FA support\n   - Configurable session timeouts (12 hours)\n\n2. Access Control (via Keto)\n   - Customer/variant scoping\n   - Organization management\n   - Role-based permissions\n   - Group management by admins\n\n3. Security (via Oathkeeper)\n   - API protection\n   - Token validation\n   - Access enforcement\n\n## Consequences\n\n### ORY\n#### Positive\n- Modern cloud-native architecture\n- Highly scalable microservices approach\n- Strong security focus\n- Kubernetes-ready\n- Language-agnostic REST APIs\n- Lower resource consumption\n\n#### Negative\n- Steeper learning curve\n- More complex initial setup\n- Multiple services to maintain\n- Less mature admin UI\n- Smaller community compared to Keycloak\n\n### Keycloak\n#### Positive\n- Mature and battle-tested\n- Single integrated solution\n- Comprehensive admin UI\n- Large community support\n- Extensive documentation\n- Enterprise support available (Red Hat)\n\n#### Negative\n- Monolithic architecture\n- Higher resource consumption\n- Java-based (higher memory footprint)\n- Less flexible for custom deployments\n- Can be complex to scale horizontally\n\n## Technical Comparison\n\n### Performance & Scalability\n#### ORY\n- Lightweight Golang based services\n- Each component independently scalable\n- Better suited for microservices architectures\n- Native Kubernetes support\n\n#### Keycloak\n- Heavier Java-based solution\n- Requires more resources\n- Clustering possible but more complex\n- Less optimal for containerized environments\n\n### Developer Experience\n#### ORY\n- Modern REST APIs\n- SDKs available for multiple languages\n- Good documentation but requires deep understanding of concepts\n- Strong DevOps focus with Kubernetes-native deployment\n- CLI tools for management\n\n#### Keycloak\n- Comprehensive admin UI reduces development overhead\n- Extensive documentation and examples\n- Larger ecosystem of plugins and extensions\n- More familiar traditional architecture\n\n## Compliance & Security\nBoth solutions:\n- GDPR compliant\n- OAuth 2.0 and OpenID Connect certified\n- Support MFA\n- Provide audit logging\n\n## Recommendation Criteria\nConsider ORY if:\n- Building a cloud-native application\n- Using microservices architecture\n- Need fine-grained control over each IAM aspect\n- Have strong DevOps capabilities\n- Resource efficiency is crucial\n\nConsider Keycloak if:\n- Need a proven, all-in-one solution\n- Have Java expertise\n- Prefer comprehensive UI administration\n- Want larger community support\n- Need enterprise support option\n\n## References\n- [ORY Documentation](https://www.ory.sh/docs/)\n- [ORY GitHub](https://github.com/ory)\n\n- [Keycloak Documentation](https://www.keycloak.org/documentation)\n- [Keycloak GitHub](https://github.com/keycloak/keycloak)\n\n## Notes\n- Implemented and deployed in production. Used by both Service UI and Central Auth systems.\n- Self-registration disabled - all user accounts to be managed by administrators\n","date":"2024-12-11T00:00:00Z","format":"Markdown","id":"1","status":"Accepted","title":" ADR-001: Identity and Access Management Platform Selection"},{"content":"# ADR-002: Central Auth System Extraction\n\n## Status\nAccepted — In Progress\nDate: 2026-03-27\n\n## Context\n\nThe authentication and authorization stack (ORY Kratos, ORY Hydra, ORY Oathkeeper, Auth Backend, Auth UI) was originally co-located inside the Energy Monitoring monorepo (`energy-monitoring/apps/auth-backend`, `energy-monitoring/apps/auth-ui`).\n\nAs the platform grew, two problems emerged:\n\n1. **Coupling**: The auth infrastructure was tightly bound to Energy Monitoring's deployment lifecycle, making it impossible to reuse for other services without duplicating it.\n2. **Scalability**: Service UI needs to register OAuth2 clients (for Flexa API access and Energy Monitoring apps) against the same ORY Hydra instance. Managing this across repositories created fragile cross-repo dependencies.\n\n## Decision\n\nExtract the full auth stack into a standalone repository and system: `central-auth`.\n\nThe `CentralAuthSystem` becomes the single source of truth for:\n- User identity management (ORY Kratos)\n- OAuth2/OIDC token issuance (ORY Hydra)\n- API gateway access rules for auth endpoints (ORY Oathkeeper)\n- BFF layer for PKCE, token exchange, session management, and user invitation (Auth Backend — NestJS)\n- Authentication UI for login/recovery flows (Auth UI — React)\n\n## Consequences\n\n### Positive\n- Auth infrastructure is shared and reused across Energy Monitoring and future services\n- Service UI can manage OAuth2 clients (Flexa API, Energy Monitoring apps) through a single Hydra instance\n- Independent deployment lifecycle from Energy Monitoring\n- Cleaner separation of concerns\n\n### Negative\n- Auth Backend code must be kept in sync between `energy-monitoring` and `central-auth` during the migration transition period\n- Energy Monitoring deployments now depend on `CentralAuthSystem` being healthy\n\n## Integration Points\n\n| Caller | CentralAuthSystem Component | Purpose |\n|---|---|---|\n| Energy Monitoring UI | Auth UI | Redirects unauthenticated users |\n| Energy Monitoring Backend | Auth UI | Redirect on missing/expired session |\n| Service UI Backend (EnergyMonitoring module) | Auth Hydra | Register new OAuth2 client applications |\n| Service UI Backend (EnergyMonitoring module) | Auth Backend | Send user invitation webhooks |\n| Service UI Backend (Flexa module) | Auth Hydra | Manage Flexa OAuth2 client credentials |\n| Auth Backend | Auth Kratos | Identity lookup and session validation |\n| Auth Backend | Auth Hydra | Token introspection and consent flows |\n\n## Migration Plan\n\n1. Create `central-auth` repository with identical Auth Backend/UI code\n2. Deploy `CentralAuthSystem` independently\n3. Update Energy Monitoring to point at `CentralAuthSystem` auth endpoints\n4. Decommission auth containers from Energy Monitoring once all traffic is migrated\n5. Update Service UI cross-service references to `CentralAuthSystem`\n\n## References\n- [ADR-001: Identity and Access Management Platform Selection](./#1)\n- Central Auth repository: `central-auth/`\n- Architecture diagram: `CentralAuthSystem` in `workspace.dsl`\n","date":"2026-03-27T00:00:00Z","format":"Markdown","id":"2","status":"Accepted","title":" ADR-002: Central Auth System Extraction"}],"sections":[{"content":"## Common test cases for users in admin panel\n\n### Main organization\n\n#### Admin\n\n1. Can add, view, edit and delete main organization users `201` | `200`\n2. Can add, view, edit and delete WLO users `201` | `200`\n3. Can add, view, edit and delete company users `201` | `200`\n4. Can view audit logs across all scopes `200`\n5. Can view all devices and see their descriptions and credentials info `200`\n6. Can access all DynamicES features:\n   - Can view all projects and simulations `200`\n   - Can create new projects for any company `201`\n   - Can create new simulations under any project if a company storage left `201`\n   - Can rerun a simulation if a company storage left `200`\n   - Can execute simulation if a company storage left `200`\n   - Cannot create new simulation when a company storage is exceeded `400`\n\n#### Support\n\n1. Can view devices from all organizations and see their descriptions and credentials info `200`\n2. Cannot manage users `401`\n3. Cannot view audit logs `401`\n4. Cannot access DynamicES features `401`\n\n---\n\n## White label organizations (WLOs)\n\n#### Admin\n\n1. Can add, view, edit and delete users within their WLO `201` | `200`\n2. Cannot view or manage users from other WLOs, companies, or the main organization `401`\n3. Can view audit logs related only to their own WLO `200`\n4. Can view devices assigned to their WLO, including descriptions and credentials `200`\n5. Cannot access DynamicES features `401`\n\n#### Support\n\n1. Can view devices assigned to their WLO, including descriptions and credentials `200`\n2. Cannot manage users `401`\n3. Cannot view audit logs `401`\n4. Cannot access DynamicES features `401`\n\n---\n\n### Companies\n\n#### Admin\n\n1. Can add, view, edit, and delete users within their company `201` | `200`\n2. Cannot view or manage users from other companies, WLOs, or the main organization `401`\n3. Cannot view audit logs (not available for companies) `401`\n4. Can access DynamicES features:\n   - Can view their own company’s projects and simulations `200`\n   - Can create new projects within their company scope `201`\n   - Can create new simulations under their company’s projects `201`\n   - Can rerun a simulation if company storage left `200`\n   - Can execute simulation if company storage left `200`\n   - Cannot add new simulations if the company limit has been exceeded `400`\n5. Cannot view device dashboard (not supported for company scope) `404` | `401`\n6. Can manage Flexa API access (if Flexa module enabled):\n   - Can create Flexa API access credentials for their company `201`\n   - Can view, update, and delete Flexa API access entries `200`\n   - Can regenerate Flexa OAuth2 credentials `200`\n7. Can manage Energy Monitoring configuration (if Energy Monitoring module enabled):\n   - Can view the Energy Monitoring configuration for their company `200`\n   - Can invite users to the Energy Monitoring dashboard `201`\n   - Can create and delete Energy Monitoring configuration `201` | `200`\n\n#### Consultant\n\n1. Can access DynamicES features within their company:\n   - Can view existing projects and simulations `200`\n   - Can add a new project within the scope of company `201`\n   - Can rerun a simulation if company storage left `200`\n   - Can execute simulation if company storage left `200`\n   - Can create new simulations under allowed projects `201`\n   - Cannot add simulations if company simulation limit is exceeded `400`\n2. Cannot view or manage users `401`\n3. Cannot view audit logs `401`\n4. Cannot view or access devices `401`\n","filename":"common-test-cases.md","format":"Markdown","order":1,"title":""},{"content":"## Keto - example requests\n\n\n### Check if user has role\n```bash\ncurl -X POST http://127.0.0.1:4455/keto-admin/relation-tuples/check \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"namespace\": \"User\",\n    \"object\": \"admin@gmail.com\",\n    \"relation\": \"roles\",\n    \"subject_id\": \"admin\"\n  }'\n```\n\n### List all user roles\n```bash\ncurl http://127.0.0.1:4455/keto/relation-tuples \\\n  -G \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  --data-urlencode 'namespace=User' \\\n  --data-urlencode 'object=admin@gmail.com' \\\n  --data-urlencode 'relation=roles'\n```\n\n### List all devices company owns\n```bash\ncurl http://127.0.0.1:4455/keto/relation-tuples \\\n  -G \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  --data-urlencode 'namespace=Device' \\\n  --data-urlencode 'relation=owner_company' \\\n  --data-urlencode 'subject=company-wlo1-1'\n```\n\n### List all WLOs belonging to Main Organization\n```bash\ncurl http://127.0.0.1:4455/keto/relation-tuples \\\n  -G \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  --data-urlencode 'namespace=WhiteLabelOrganization' \\\n  --data-urlencode 'relation=belongs_to' \\\n  --data-urlencode 'subject=mo-1'\n```\n\n### List all companies in a WLO\n```bash\ncurl http://127.0.0.1:4455/keto/relation-tuples \\\n  -G \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  --data-urlencode 'namespace=Company' \\\n  --data-urlencode 'relation=belongs_to' \\\n  --data-urlencode 'subject=wlo-1'\n```\n\n### Create Main Organization admin\n```bash\ncurl -X PUT http://127.0.0.1:4455/keto-admin/relation-tuples \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"namespace\": \"MainOrganization\",\n    \"object\": \"mo-1\",\n    \"relation\": \"admins\",\n    \"subject_id\": \"admin@gmail.com\",\n    \"subject_set\": {\n      \"namespace\": \"User\",\n      \"object\": \"admin@gmail.com\",\n      \"relation\": \"roles\",\n      \"subject_id\": \"admin\"\n    }\n  }'\n```\n\n### Create WLO\n```bash\ncurl -X PUT http://127.0.0.1:4455/keto-admin/relation-tuples \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"namespace\": \"WhiteLabelOrganization\",\n    \"object\": \"wlo-1\",\n    \"relation\": \"belongs_to\",\n    \"subject_id\": \"mo-1\",\n    \"subject_set\": {\n      \"namespace\": \"MainOrganization\",\n      \"subject_id\": \"mo-1\"\n    }\n  }'\n```\n\n### Add WLO support user to WLO\n```bash\ncurl -X PUT http://127.0.0.1:4455/keto-admin/relation-tuples \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"namespace\": \"WhiteLabelOrganization\",\n    \"object\": \"wlo-1\",\n    \"relation\": \"support_wlo_users\",\n    \"subject_id\": \"support-wlo@wlo-1.com\",\n    \"subject_set\": {\n      \"namespace\": \"User\",\n      \"subject_id\": \"support-wlo@wlo-1.com\"\n    }\n  }'\n```\n\n### Add installer to company\n```bash\ncurl -X PUT http://127.0.0.1:4455/keto-admin/relation-tuples \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"namespace\": \"Company\",\n    \"object\": \"company-wlo1-1\",\n    \"relation\": \"installers\",\n    \"subject_id\": \"installer-1@gmail.com\",\n    \"subject_set\": {\n      \"namespace\": \"User\",\n      \"subject_id\": \"installer-1@gmail.com\"\n    }\n  }'\n```\n\n### Assign device to company\n```bash\ncurl -X PUT http://127.0.0.1:4455/keto-admin/relation-tuples \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"namespace\": \"Device\",\n    \"object\": \"1U0022_QC-1\",\n    \"relation\": \"owner_company\",\n    \"subject_id\": \"company-wlo1-1\",\n    \"subject_set\": {\n      \"namespace\": \"Company\",\n      \"subject_id\": \"company-wlo1-1\"\n    }\n  }'\n```\n\n### Assign article to WLO\n```bash\ncurl -X PUT http://127.0.0.1:4455/keto-admin/relation-tuples \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"namespace\": \"Article\",\n    \"object\": \"1U0022_QC\",\n    \"relation\": \"belongs_to_wlo\",\n    \"subject_id\": \"wlo-1\",\n    \"subject_set\": {\n      \"namespace\": \"WhiteLabelOrganization\",\n      \"subject_id\": \"wlo-1\"\n    }\n  }'\n```\n","filename":"keto.md","format":"Markdown","order":2,"title":""},{"content":"## Kratos - example requests\n\n### Create a new user by administrator\n\n```bash\n   curl -X POST http://127.0.0.1:4455/kratos-admin/admin/identities \\\n     -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n     -H \"Authorization: Bearer jwt-token\" \\\n     -H 'Content-Type: application/json' \\\n     -d '{\n       \"schema_id\": \"default\",\n       \"traits\": {\n         \"email\": \"admin@example.com\"\n       },\n       \"credentials\": {\n         \"password\": {\n           \"config\": {\n             \"password\": \"secure-password\"\n           }\n         }\n       },\n       \"metadata_admin\": {\n         \"admin\": true\n       }\n     }'\n```\n\n### Get all users\n\n```bash\ncurl -X GET http://127.0.0.1:4455/kratos-admin/admin/identities \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H 'Content-Type: application/json' \\\n  -v\n```\n\n### Get a user by id\n\n```bash\ncurl -X GET http://127.0.0.1:4455/kratos-admin/identities/{USER_ID} \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H 'Content-Type: application/json' \\\n  -v\n```\n\n### Get users with pagination\n\n```bash\ncurl -X GET 'http://127.0.0.1:4455/kratos-admin/identities?page_size=10&page_token=0' \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\ OR\n  -H \"Authorization: Bearer jwt-token\" \\\n  -H 'Content-Type: application/json' \\\n  -v\n```\n","filename":"kratos.md","format":"Markdown","order":3,"title":""},{"content":"\n## Permissions structure\n\n``` mermaid\n    graph TD\n    %% Main Organization\n    MO[MainOrganization: mo-1] -->|admins| U1[User: admin-1]\n    MO -->|support_org_users| U2[User: support-1]\n\n    %% WLO1 Subgraph\n    subgraph WLO_1[White Label Organization: wlo1]\n        WLO1[WhiteLabelOrganization: wlo-1]\n        A1[Article: article_123]\n        C1[Company: company-1]\n        U3[User: wlo-support-1]\n        U5[User: installer-1]\n        D1[Device: article_123_device_1]\n        U7[User: device-owner-1]\n        D4[Device: article_123_device_4]\n    end\n\n    %% WLO2 Subgraph\n    subgraph WLO_2[White Label Organization: wlo-2]\n        WLO2[WhiteLabelOrganization: wlo-2]\n        A2[Article: article_456]\n        C2[Company: company-2]\n        U4[User: wlo-support-2]\n        U6[User: installer-2]\n        D2[Device: article_456_device_2]\n        D3[Device: article_456_device_3]\n        U8[User: device-owner-2]\n    end\n\n    %% Relationships\n    WLO1 -->|belongs_to| MO\n    WLO2 -->|belongs_to| MO\n    A1 -->|belongs_to_wlo| WLO1\n    A2 -->|belongs_to| WLO2\n    WLO1 -->|support_wlo_users| U3\n    WLO2 -->|support_wlo_users| U4\n\n    \n    C1 -->|belongs_to| WLO1\n    C2 -->|belongs_to| WLO2\n    C1 -->|installers| U5\n    C2 -->|installers| U6\n    D1 -->|owner_company| C1\n    D1 -->|owner_user| U7\n    D1 -->|article| A1\n    D2 -->|owner_user| U8\n    D2 -->|owner_company| C2\n    D2 -->|article| A2\n    D4 -->|owner_wlo| WLO1\n    D3 -->|owner_company| C2\n    D3 -->|article| A2\n    D3 -->|owner_user| U3\n    D3 -->|owner_wlo| WLO1\n    D4 -->|article| A1\n\n\n    %% Styling\n    classDef organization fill:#d12a6a,stroke:#333,stroke-width:2px\n    classDef wlo fill:#c066c0,stroke:#333,stroke-width:2px\n    classDef user fill:#4a69bd,stroke:#333,stroke-width:1px\n    classDef device fill:#218c74,stroke:#333,stroke-width:1px\n    classDef article fill:#cc8e35,stroke:#333,stroke-width:1px\n\n    class MO organization\n    class WLO1,WLO2 wlo\n    class U1,U2,U3,U4,U5,U6,U7,U8,U9 user\n    class D1,D2,D3,D4 device\n    class A1,A2 article\n```\n\n\n### Main Organization relationships\nMainOrganization:mo-1#admins@User:admin-1\nMainOrganization:mo-1#support_org_users@User:support-1\n\nUse case:\n- User admin-1 is an admin of MainOrganization mo-1\n- User support-1 is a support user of MainOrganization mo-1\n\n### WLO relationships\nWhiteLabelOrganization:wlo-1#belongs_to@MainOrganization:mo-1\nWhiteLabelOrganization:wlo-2#belongs_to@MainOrganization:mo-1\nWhiteLabelOrganization:wlo-1#support_wlo_users@User:wlo-support-1\nWhiteLabelOrganization:wlo-2#support_wlo_users@User:wlo-support-2\nWhiteLabelOrganization:wlo-1#device_article@article_123\nWhiteLabelOrganization:wlo-2#device_article@article_456\n\nUse case:\n- WhiteLabelOrganization wlo-1 belongs to MainOrganization mo-1\n- WhiteLabelOrganization wlo-2 belongs to MainOrganization mo-1\n- User wlo-support-1 is a support user of WhiteLabelOrganization wlo-1\n- User wlo-support-2 is a support user of WhiteLabelOrganization wlo-2\n- WhiteLabelOrganization wlo-1 has device article article_123\n- WhiteLabelOrganization wlo-2 has device article article_456\n\n### Article relationships\nArticle:article_123#belongs_to_wlo@WhiteLabelOrganization:wlo-1\nArticle:article_456#belongs_to_wlo@WhiteLabelOrganization:wlo-2\n\n#### Company relationships\nCompany:company-1#belongs_to@WhiteLabelOrganization:wlo-1\nCompany:company-2#belongs_to@WhiteLabelOrganization:wlo-2\nCompany:company-1#installers@User:installer-1\nCompany:company-2#installers@User:installer-2\n\nUse case:\n- Company company-1 belongs to WhiteLabelOrganization wlo-1\n- Company company-2 belongs to WhiteLabelOrganization wlo-2\n- User installer-1 is an installer of Company company-1\n- User installer-2 is an installer of Company company-2\n\n### Device relationships\nDevice:article_123_device_1#owner_company@Company:company-1\nDevice:article_123_device_1#owner_user@User:device-owner-1\nDevice:article_123_device_1#article@article_123\n\nDevice:article_456_device_2#owner_company@Company:company-2\nDevice:article_456_device_2#owner_user@User:device-owner-2\nDevice:article_456_device_2#article@article_456\n\nDevice:article_456_device_3#owner_company@Company:company-2\nDevice:article_456_device_3#owner_user@User:wlo-support-1\nDevice:article_456_device_3#article@article_456\n\nDevice:article_123_device_4#owner_wlo@WhiteLabelOrganization:wlo-1\nDevice:article_123_device_4#article@article_123\n\nUse case:\n- Company company-1 owns Device article_123_device_1\n- User device-owner-1 owns Device article_123_device_1\n- Article article_123 is the article for Device article_123_device_1\n\n- Company company-2 owns Device article_456_device_2\n- User device-owner-2 owns Device article_456_device_2\n- Article article_456 is the article for Device article_456_device_2\n\n- Company company-2 owns Device article_456_device_3\n- User wlo-support-1 owns Device article_456_device_3\n- Article article_456 is the article for Device article_456_device_3\n\n- Device article_123_device_4 is directly owned by WhiteLabelOrganization wlo-1\n- Article article_123 is the article for Device article_123_device_4\n","filename":"permissions.md","format":"Markdown","order":4,"title":""},{"content":"# service-ui-api — Example Requests\n\nAll endpoints are exposed via the Ory Oathkeeper proxy at `http://127.0.0.1:4455/api/*` unless otherwise noted.\nAuthentication can be provided as a session cookie or JWT Bearer token.\n\n---\n\n## Health\n\n### Get health (no authentication required)\n\n```bash\ncurl -X GET http://127.0.0.1:8080/api/health\n```\n\n---\n\n## Session\n\n### Get current user session\n\n```bash\ncurl -X GET http://127.0.0.1:4455/api/account/session \\\n  -H \"Cookie: ory_kratos_session=session-cookie-value\"\n```\n\nReturns a Kratos `Session` object including identity, roles, and assurance level (aal2 required).\n\n---\n\n## Devices\n\n### List devices (requires authentication)\n\n```bash\ncurl -X GET \"http://127.0.0.1:4455/api/devices?page=1&limit=10\" \\\n  -H \"Cookie: ory_session=session-cookie-value\"\n  # OR: -H \"Authorization: Bearer jwt-token\"\n```\n\n### Get device by serial number\n\n```bash\ncurl -X GET http://127.0.0.1:4455/api/devices/1U0022_BASE-20 \\\n  -H \"Cookie: ory_session=session-cookie-value\"\n```\n\n---\n\n## Flexa API Access\n\nAll Flexa endpoints require authentication.\n\n### List Flexa API access entries\n\n```bash\ncurl -X GET \"http://127.0.0.1:4455/api/flexa/api-access?page=1&limit=10\" \\\n  -H \"Cookie: ory_session=session-cookie-value\"\n```\n\n### Create Flexa API access credentials\n\n```bash\ncurl -X POST http://127.0.0.1:4455/api/flexa/api-access \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"My Flexa Integration\",\n    \"scope\": [\"consolinno\", \"demo\"]\n  }'\n```\n\nResponse includes `client_id` and `client_secret` (secret only returned on creation).\n\n### Get Flexa API access entry by ID\n\n```bash\ncurl -X GET http://127.0.0.1:4455/api/flexa/api-access/{UUID} \\\n  -H \"Cookie: ory_session=session-cookie-value\"\n```\n\n### Update Flexa API access (scopes/name)\n\n```bash\ncurl -X PATCH http://127.0.0.1:4455/api/flexa/api-access/{UUID} \\\n  -H \"Cookie: ory_session=session-cookie-value\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"scope\": [\"consolinno\"]\n  }'\n```\n\n### Regenerate Flexa OAuth2 credentials\n\n```bash\ncurl -X PATCH http://127.0.0.1:4455/api/flexa/api-access/{UUID}/credentials \\\n  -H \"Cookie: ory_session=session-cookie-value\"\n```\n\nReturns new `client_id` and `client_secret`.\n\n### Delete Flexa API access entry\n\n```bash\ncurl -X DELETE http://127.0.0.1:4455/api/flexa/api-access/{UUID} \\\n  -H \"Cookie: ory_session=session-cookie-value\"\n```\n\n---\n\n## Webhook Receiver\n\nThe webhook endpoint is public but validates a signed JWT in the `X-Token` header.\n\nJWT requirements:\n- `iss`: `service-ui-webhook-issuer`\n- `aud`: `service-ui-webhook-api`\n- `scope`: must include `webhook:write`\n\n### Sync Flexa features (sent by flexa-gateway-config CI after deployment)\n\n```bash\ncurl -X POST http://127.0.0.1:8080/api/webhook \\\n  -H \"X-Token: <signed-jwt>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"payload\": {\n      \"topic\": \"sync-features\",\n      \"module\": \"Flexa\",\n      \"data\": {\n        \"apps\": [\n          { \"scope\": \"consolinno\", \"url\": \"https://flexa.consolinno.api.services.consolinno-it.de\" },\n          { \"scope\": \"demo\", \"url\": \"https://flexa.demo.api.services.consolinno-it.de\" }\n        ]\n      }\n    }\n  }'\n```\n\n### Device claiming webhook\n\n```bash\ncurl -X POST http://127.0.0.1:8080/api/webhook \\\n  -H \"X-Token: <signed-jwt>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"payload\": {\n      \"topic\": \"device-claiming\",\n      \"module\": \"Devices\",\n      \"data\": { ... }\n    }\n  }'\n```\n\n### Audit log webhook\n\n```bash\ncurl -X POST http://127.0.0.1:8080/api/webhook \\\n  -H \"X-Token: <signed-jwt>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"payload\": {\n      \"topic\": \"audit-log\",\n      \"module\": \"ServiceUI\",\n      \"data\": { ... }\n    }\n  }'\n```\n","filename":"service-ui-api.md","format":"Markdown","order":5,"title":""}]},"id":1,"lastModifiedAgent":"structurizr-ui","lastModifiedDate":"2026-06-12T09:13:19Z","lastModifiedUser":"m.waszkiewicz","model":{"customElements":[{"description":"responsible for charging the EV","id":"1","metadata":"Hardware","name":"Charger","properties":{"structurizr.dsl.identifier":"charger"},"relationships":[{"description":"communicates with","destinationId":"17","id":"274","sourceId":"1","tags":"Relationship","technology":"TBD"},{"description":"communicates with","destinationId":"16","id":"275","linkedRelationshipId":"274","sourceId":"1","technology":"TBD"}],"tags":"Element,Hardware"},{"description":"responsible for storing energy","id":"2","metadata":"Hardware","name":"Battery Storage","properties":{"structurizr.dsl.identifier":"battery"},"relationships":[{"description":"communicates with","destinationId":"17","id":"270","sourceId":"2","tags":"Relationship","technology":"TBD"},{"description":"communicates with","destinationId":"16","id":"271","linkedRelationshipId":"270","sourceId":"2","technology":"TBD"}],"tags":"Element,Hardware"},{"description":"responsible for generating energy","id":"3","metadata":"Hardware","name":"PV","properties":{"structurizr.dsl.identifier":"pv"},"relationships":[{"description":"communicates with","destinationId":"17","id":"268","sourceId":"3","tags":"Relationship","technology":"TBD"},{"description":"communicates with","destinationId":"16","id":"269","linkedRelationshipId":"268","sourceId":"3","technology":"TBD"}],"tags":"Element,Hardware"},{"description":"responsible for heating the house","id":"4","metadata":"Hardware","name":"Heat Pump","properties":{"structurizr.dsl.identifier":"heatpump"},"relationships":[{"description":"communicates with","destinationId":"17","id":"272","sourceId":"4","tags":"Relationship","technology":"TBD"},{"description":"communicates with","destinationId":"16","id":"273","linkedRelationshipId":"272","sourceId":"4","technology":"TBD"}],"tags":"Element,Hardware"}],"people":[{"description":"a user registered in the Consolinno organization [Admin, Support]","id":"5","name":"Consolinno User","properties":{"structurizr.dsl.identifier":"orgUser"},"relationships":[{"description":"interacts with","destinationId":"60","id":"253","properties":{"structurizr.dsl.identifier":"orgUserServiceUI"},"sourceId":"5","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"59","id":"254","linkedRelationshipId":"253","sourceId":"5","technology":"JSON/HTTPS"}],"tags":"Element,Person"},{"description":"a user registered in the whitelabel organization [Admin, Support]","id":"6","name":"Whitelabel Organization User","properties":{"structurizr.dsl.identifier":"wloUser"},"relationships":[{"description":"interacts with","destinationId":"60","id":"255","properties":{"structurizr.dsl.identifier":"wloUserServiceUI"},"sourceId":"6","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"59","id":"256","linkedRelationshipId":"255","sourceId":"6","technology":"JSON/HTTPS"},{"description":"provides device credentials to","destinationId":"18","id":"257","properties":{"structurizr.dsl.identifier":"wloUserHEMSDesktopApp"},"sourceId":"6","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"provides device credentials to","destinationId":"16","id":"258","linkedRelationshipId":"257","sourceId":"6","technology":"JSON/HTTPS"}],"tags":"Element,Person"},{"description":"User registered in the company [Admin, Support, Installer, Consultant]","id":"7","name":"Service & Installer Company User","properties":{"structurizr.dsl.identifier":"companyUser"},"relationships":[{"description":"interacts with","destinationId":"60","id":"259","properties":{"structurizr.dsl.identifier":"companyUserServiceUI"},"sourceId":"7","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"59","id":"260","linkedRelationshipId":"259","sourceId":"7","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"61","id":"261","properties":{"structurizr.dsl.identifier":"companyUserSelfClaimingUI"},"sourceId":"7","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"18","id":"262","properties":{"structurizr.dsl.identifier":"companyUserHEMSMobileApp"},"sourceId":"7","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"16","id":"263","linkedRelationshipId":"262","sourceId":"7","technology":"JSON/HTTPS"}],"tags":"Element,Person"},{"description":"Is the user who has access to the energy monitoring dashboard registered by the company admin [User, Admin] - no access to Service UI","id":"8","name":"Energy Monitoring User","properties":{"structurizr.dsl.identifier":"energyMonitoringUser"},"relationships":[{"description":"interacts with","destinationId":"26","id":"264","properties":{"structurizr.dsl.identifier":"energyMonitoringUserEnergyMonitoringUI"},"sourceId":"8","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"25","id":"265","linkedRelationshipId":"264","sourceId":"8","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"35","id":"266","properties":{"structurizr.dsl.identifier":"energyMonitoringUserEnergyMonitoringAuth"},"sourceId":"8","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"34","id":"267","linkedRelationshipId":"266","sourceId":"8","technology":"JSON/HTTPS"}],"tags":"Element,Person"}],"properties":{"structurizr.groupSeparator":"/"},"softwareSystems":[{"description":"Backend responsible for providing energy optimization.","documentation":{},"group":"Dynamic ES","id":"9","name":"Dynamic ES","properties":{"structurizr.dsl.identifier":"DynamicESSystem"},"tags":"Element,Software System,Dynamic ES"},{"containers":[{"description":"Stores device and deployment data.","documentation":{},"id":"11","name":"Mender IO MongoDB","properties":{"structurizr.dsl.identifier":"MenderSystem.MongoDB"},"tags":"Element,Container,Database","technology":"MongoDB"},{"description":"API and device management services.","documentation":{},"id":"12","name":"Mender IO Backend Services","properties":{"structurizr.dsl.identifier":"MenderSystem.MenderBackend"},"relationships":[{"description":"reads and writes to","destinationId":"11","id":"15","sourceId":"12","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Container,Service","technology":"Go"},{"description":"Web UI for Mender management.","documentation":{},"id":"13","name":"Mender IO Web App","properties":{"structurizr.dsl.identifier":"MenderSystem.MenderWebApp"},"relationships":[{"description":"interacts with","destinationId":"12","id":"14","sourceId":"13","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Container,WebApp","technology":"React"}],"description":"Responsible for managing devices and OTA updates.","documentation":{},"group":"Mender IO","id":"10","name":"Mender IO","properties":{"structurizr.dsl.identifier":"MenderSystem"},"tags":"Element,Software System,Mender IO"},{"containers":[{"description":"Nymea-based HEMS controller.","documentation":{},"id":"17","name":"Lefleat HEMS Nymea Core","properties":{"structurizr.dsl.identifier":"HEMSSystem.LefleatHEMS"},"relationships":[{"description":"sends device data to","destinationId":"12","id":"190","properties":{"structurizr.dsl.identifier":"hemsLefleatHEMSMenderBackendRelationships"},"sourceId":"17","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"sends device data to","destinationId":"10","id":"191","linkedRelationshipId":"190","sourceId":"17","technology":"JSON/HTTPS"}],"tags":"Element,Container,Hardware","technology":"Nymea based"},{"components":[{"description":"Handles device self-claiming flow.","documentation":{},"id":"19","name":"Self Claiming Plugin","properties":{"structurizr.dsl.identifier":"HEMSSystem.HEMSApp.SelfClaimingPlugin"},"relationships":[{"description":"claims devices","destinationId":"61","id":"200","properties":{"structurizr.dsl.identifier":"hemsAppSelfClaimingPluginRelationships"},"sourceId":"19","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"claims devices","destinationId":"59","id":"201","linkedRelationshipId":"200","sourceId":"19","technology":"JSON/HTTPS"},{"description":"stores code_challenge in","destinationId":"71","id":"206","sourceId":"19","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Component","technology":"Nymea"}],"description":"Mobile app for HEMS control","documentation":{},"id":"18","name":"Consolinno HEMS Mobile App","properties":{"structurizr.dsl.identifier":"HEMSSystem.HEMSApp"},"relationships":[{"description":"communicates with LefleatHEMS via","destinationId":"20","id":"21","sourceId":"18","tags":"Relationship","technology":"HTTPS"},{"description":"claims devices","destinationId":"61","id":"202","linkedRelationshipId":"200","sourceId":"18","technology":"JSON/HTTPS"},{"description":"claims devices","destinationId":"59","id":"203","linkedRelationshipId":"200","sourceId":"18","technology":"JSON/HTTPS"},{"description":"stores code_challenge in","destinationId":"71","id":"207","linkedRelationshipId":"206","sourceId":"18","technology":"JSON/HTTPS"}],"tags":"Element,Container,MobileApp","technology":"Nymea based"},{"description":"Secures and forwards traffic to Lefleat HEMS.","documentation":{},"id":"20","name":"Consolinno HEMS Tunnel Proxy","properties":{"structurizr.dsl.identifier":"HEMSSystem.TunnelProxy"},"relationships":[{"description":"forward requests to","destinationId":"17","id":"22","sourceId":"20","tags":"Relationship","technology":"HTTPS"}],"tags":"Element,Container,Service","technology":"TBD"}],"description":"Home energy management and device control.","documentation":{},"group":"Consolinno Home Energy Management System","id":"16","name":"Home Energy Management System","properties":{"structurizr.dsl.identifier":"HEMSSystem"},"relationships":[{"description":"sends device data to","destinationId":"12","id":"192","linkedRelationshipId":"190","sourceId":"16","technology":"JSON/HTTPS"},{"description":"sends device data to","destinationId":"10","id":"193","linkedRelationshipId":"190","sourceId":"16","technology":"JSON/HTTPS"},{"description":"claims devices","destinationId":"61","id":"204","linkedRelationshipId":"200","sourceId":"16","technology":"JSON/HTTPS"},{"description":"claims devices","destinationId":"59","id":"205","linkedRelationshipId":"200","sourceId":"16","technology":"JSON/HTTPS"},{"description":"stores code_challenge in","destinationId":"71","id":"208","linkedRelationshipId":"206","sourceId":"16","technology":"JSON/HTTPS"}],"tags":"Element,Software System,Home Energy Management System"},{"containers":[{"description":"Compatible with S3","documentation":{},"id":"24","name":"SysEleven Object Storage","properties":{"structurizr.dsl.identifier":"SysElevenSystem.ObjectStorage"},"tags":"Element,Container,Service","technology":"S3"}],"description":"Cloud object storage (S3-compatible).","documentation":{},"group":"SysEleven Cloud","id":"23","name":"SysEleven","properties":{"structurizr.dsl.identifier":"SysElevenSystem"},"tags":"Element,Software System,SysEleven"},{"containers":[{"description":"Whitelabel Application which allows users to monitor their energy data","documentation":{},"id":"26","name":"Energy Monitoring UI","properties":{"structurizr.dsl.identifier":"EnergyMonitoringSystem.EnergyMonitoringUI"},"relationships":[{"description":"interacts with","destinationId":"27","id":"31","properties":{"structurizr.dsl.identifier":"energyMonitoringUIRelationships"},"sourceId":"26","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Container,WebApp","technology":"React"},{"components":[{"description":"Responsible for handling energy monitoring configuration","documentation":{},"id":"28","name":"Energy Monitoring Module","properties":{"structurizr.dsl.identifier":"EnergyMonitoringSystem.EnergyMonitoringBackend.EnergyMonitoring"},"relationships":[{"description":"reads and writes to","destinationId":"30","id":"32","properties":{"structurizr.dsl.identifier":"energyMonitoringBackendEnergyMonitoringDatabaseRelationships"},"sourceId":"28","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Component","technology":"Nest.js"},{"description":"Responsible for forwarding requests to FluxCapacitor","documentation":{},"id":"29","name":"FluxCapacitor Executor","properties":{"structurizr.dsl.identifier":"EnergyMonitoringSystem.EnergyMonitoringBackend.FluxCapacitorExecutor"},"relationships":[{"description":"forwards requests to Flux Capacitor","destinationId":"51","id":"215","properties":{"structurizr.dsl.identifier":"energyMonitoringBackendFluxCapacitorRelationships"},"sourceId":"29","tags":"Relationship","technology":"GraphQL"},{"description":"forwards requests to Flux Capacitor","destinationId":"50","id":"216","linkedRelationshipId":"215","sourceId":"29","technology":"GraphQL"},{"description":"forwards requests to Flux Capacitor","destinationId":"49","id":"217","linkedRelationshipId":"215","sourceId":"29","technology":"GraphQL"}],"tags":"Element,Component","technology":"Nest.js"}],"description":"Responsible for handling energy monitoring business logic","documentation":{},"id":"27","name":"Energy Monitoring Backend","properties":{"structurizr.dsl.identifier":"EnergyMonitoringSystem.EnergyMonitoringBackend"},"relationships":[{"description":"reads and writes to","destinationId":"30","id":"33","linkedRelationshipId":"32","sourceId":"27","technology":"JSON/HTTPS"},{"description":"forwards requests to Flux Capacitor","destinationId":"51","id":"218","linkedRelationshipId":"215","sourceId":"27","technology":"GraphQL"},{"description":"forwards requests to Flux Capacitor","destinationId":"50","id":"219","linkedRelationshipId":"215","sourceId":"27","technology":"GraphQL"},{"description":"forwards requests to Flux Capacitor","destinationId":"49","id":"220","linkedRelationshipId":"215","sourceId":"27","technology":"GraphQL"},{"description":"redirects unauthenticated users to","destinationId":"35","id":"224","properties":{"structurizr.dsl.identifier":"energyMonitoringBackendCentralAuthRedirectRelationships"},"sourceId":"27","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"redirects unauthenticated users to","destinationId":"34","id":"225","linkedRelationshipId":"224","sourceId":"27","technology":"JSON/HTTPS"}],"tags":"Element,Container,Service","technology":"Nest.js"},{"description":"Responsible for storing energy monitoring configuration data","documentation":{},"id":"30","name":"Energy Monitoring Database","properties":{"structurizr.dsl.identifier":"EnergyMonitoringSystem.EnergyMonitoringDatabase"},"tags":"Element,Container,Database","technology":"PostgreSql"}],"description":"Whitelabel energy monitoring dashboards using Central Auth for authentication.","documentation":{},"group":"Energy Monitoring","id":"25","name":"Energy Monitoring","properties":{"structurizr.dsl.identifier":"EnergyMonitoringSystem"},"relationships":[{"description":"forwards requests to Flux Capacitor","destinationId":"51","id":"221","linkedRelationshipId":"215","sourceId":"25","technology":"GraphQL"},{"description":"forwards requests to Flux Capacitor","destinationId":"50","id":"222","linkedRelationshipId":"215","sourceId":"25","technology":"GraphQL"},{"description":"forwards requests to Flux Capacitor","destinationId":"49","id":"223","linkedRelationshipId":"215","sourceId":"25","technology":"GraphQL"},{"description":"redirects unauthenticated users to","destinationId":"35","id":"226","linkedRelationshipId":"224","sourceId":"25","technology":"JSON/HTTPS"},{"description":"redirects unauthenticated users to","destinationId":"34","id":"227","linkedRelationshipId":"224","sourceId":"25","technology":"JSON/HTTPS"}],"tags":"Element,Software System,Energy Monitoring"},{"containers":[{"description":"Allows users to authenticate into energy monitoring and other apps.","documentation":{},"id":"35","name":"Auth UI","properties":{"structurizr.dsl.identifier":"CentralAuthSystem.AuthUI"},"relationships":[{"description":"interacts with","destinationId":"36","id":"41","properties":{"structurizr.dsl.identifier":"authUIRelationships"},"sourceId":"35","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Container,WebApp","technology":"React"},{"description":"Protects auth endpoints; routes to OauthBackend and validates tokens.","documentation":{},"id":"36","name":"Auth Oathkeeper API Gateway","properties":{"structurizr.dsl.identifier":"CentralAuthSystem.AuthGateway"},"relationships":[{"description":"interacts with","destinationId":"39","id":"42","properties":{"structurizr.dsl.identifier":"authGatewayAuthHydraRelationships"},"sourceId":"36","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"37","id":"43","properties":{"structurizr.dsl.identifier":"authGatewayOauthBackendRelationships"},"sourceId":"36","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"38","id":"44","properties":{"structurizr.dsl.identifier":"authGatewayAuthKratosRelationships"},"sourceId":"36","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Container,Service","technology":"ORY Oathkeeper"},{"description":"BFF layer: PKCE, token exchange, session management, user and application management. Also handles Kratos webhook events (user invitations, recovery).","documentation":{},"id":"37","name":"Auth Backend","properties":{"structurizr.dsl.identifier":"CentralAuthSystem.OauthBackend"},"relationships":[{"description":"interacts with","destinationId":"39","id":"47","properties":{"structurizr.dsl.identifier":"oauthBackendHydraRelationships"},"sourceId":"37","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"interacts with","destinationId":"38","id":"48","properties":{"structurizr.dsl.identifier":"oauthBackendKratosRelationships"},"sourceId":"37","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads user apps from","destinationId":"69","id":"237","properties":{"structurizr.dsl.identifier":"centralAuthOauthBackendServiceUIRelationships"},"sourceId":"37","tags":"Relationship","technology":"REST/JSON"},{"description":"reads user apps from","destinationId":"62","id":"238","linkedRelationshipId":"237","sourceId":"37","technology":"REST/JSON"},{"description":"reads user apps from","destinationId":"59","id":"239","linkedRelationshipId":"237","sourceId":"37","technology":"REST/JSON"}],"tags":"Element,Container,Service","technology":"Nest.js"},{"description":"Responsible for handling authentication","documentation":{},"id":"38","name":"Auth ORY Kratos","properties":{"structurizr.dsl.identifier":"CentralAuthSystem.AuthKratos"},"relationships":[{"description":"reads and writes to","destinationId":"40","id":"45","properties":{"structurizr.dsl.identifier":"authKratosDatabaseRelationships"},"sourceId":"38","tags":"Relationship","technology":"SQL/TCP"}],"tags":"Element,Container,Service","technology":"ORY Kratos"},{"description":"Responsible for handling client applications and OAuth2.0","documentation":{},"id":"39","name":"Auth ORY Hydra","properties":{"structurizr.dsl.identifier":"CentralAuthSystem.AuthHydra"},"relationships":[{"description":"reads and writes to","destinationId":"40","id":"46","properties":{"structurizr.dsl.identifier":"authHydraDatabaseRelationships"},"sourceId":"39","tags":"Relationship","technology":"SQL/TCP"}],"tags":"Element,Container,Service","technology":"ORY Hydra"},{"description":"Responsible for storing authentication data","documentation":{},"id":"40","name":"Auth Database","properties":{"structurizr.dsl.identifier":"CentralAuthSystem.AuthDatabase"},"tags":"Element,Container,Database","technology":"PostgreSql"}],"description":"Centralized authentication and authorization system shared across Energy Monitoring and other services. Provides OAuth2/OIDC via ORY Hydra, identity via ORY Kratos, and a BFF layer.","documentation":{},"group":"Central Auth","id":"34","name":"Central Auth","properties":{"structurizr.dsl.identifier":"CentralAuthSystem"},"relationships":[{"description":"reads user apps from","destinationId":"69","id":"240","linkedRelationshipId":"237","sourceId":"34","technology":"REST/JSON"},{"description":"reads user apps from","destinationId":"62","id":"241","linkedRelationshipId":"237","sourceId":"34","technology":"REST/JSON"},{"description":"reads user apps from","destinationId":"59","id":"242","linkedRelationshipId":"237","sourceId":"34","technology":"REST/JSON"}],"tags":"Element,Software System,Central Auth"},{"containers":[{"components":[{"description":"GraphQL API for device data.","documentation":{},"id":"51","name":"Flux Grid GraphQL","properties":{"structurizr.dsl.identifier":"FluxCapacitorSystem.FluxCapacitor.GraphQL"},"relationships":[{"description":"reads device data from","destinationId":"11","id":"167","properties":{"structurizr.dsl.identifier":"fluxGraphQLMongoDBRelationships"},"sourceId":"51","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"10","id":"168","linkedRelationshipId":"167","sourceId":"51","technology":"JSON/HTTPS"},{"description":"reads and writes to","destinationId":"88","id":"184","properties":{"structurizr.dsl.identifier":"fluxCapacitorGraphQLDBRelationships"},"sourceId":"51","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads and writes to","destinationId":"84","id":"185","linkedRelationshipId":"184","sourceId":"51","technology":"JSON/HTTPS"},{"description":"reads and writes to","destinationId":"59","id":"186","linkedRelationshipId":"184","sourceId":"51","technology":"JSON/HTTPS"}],"tags":"Element,Component","technology":"Rust"},{"description":"REST API for device data.","documentation":{},"id":"52","name":"Flux Grid Rest API","properties":{"structurizr.dsl.identifier":"FluxCapacitorSystem.FluxCapacitor.RestAPI"},"relationships":[{"description":"reads device data from","destinationId":"11","id":"173","properties":{"structurizr.dsl.identifier":"fluxRestMongoDBRelationships"},"sourceId":"52","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"10","id":"174","linkedRelationshipId":"173","sourceId":"52","technology":"JSON/HTTPS"},{"description":"reads and writes to","destinationId":"88","id":"175","properties":{"structurizr.dsl.identifier":"fluxCapacitorDBRelationships"},"sourceId":"52","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"176","linkedRelationshipId":"175","sourceId":"52","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"59","id":"177","linkedRelationshipId":"175","sourceId":"52","technology":"SQL/TCP"}],"tags":"Element,Component","technology":"Rust"}],"description":"Device data and Flux Capacitor APIs.","documentation":{},"id":"50","name":"Flux Grid Capacitor","properties":{"structurizr.dsl.identifier":"FluxCapacitorSystem.FluxCapacitor"},"relationships":[{"description":"reads device data from","destinationId":"11","id":"169","linkedRelationshipId":"167","sourceId":"50","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"10","id":"170","linkedRelationshipId":"167","sourceId":"50","technology":"JSON/HTTPS"},{"description":"reads and writes to","destinationId":"88","id":"178","linkedRelationshipId":"175","sourceId":"50","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"179","linkedRelationshipId":"175","sourceId":"50","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"59","id":"180","linkedRelationshipId":"175","sourceId":"50","technology":"SQL/TCP"}],"tags":"Element,Container,Service","technology":"Rust"}],"description":"Device data and energy APIs. Provides GraphQL and REST APIs for device data, consumed by Service UI and Energy Monitoring.","documentation":{},"group":"Flux Grid Capacitor","id":"49","name":"Flux Grid Capacitor","properties":{"structurizr.dsl.identifier":"FluxCapacitorSystem"},"relationships":[{"description":"reads device data from","destinationId":"11","id":"171","linkedRelationshipId":"167","sourceId":"49","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"10","id":"172","linkedRelationshipId":"167","sourceId":"49","technology":"JSON/HTTPS"},{"description":"reads and writes to","destinationId":"88","id":"181","linkedRelationshipId":"175","sourceId":"49","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"182","linkedRelationshipId":"175","sourceId":"49","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"59","id":"183","linkedRelationshipId":"175","sourceId":"49","technology":"SQL/TCP"}],"tags":"Element,Software System,Flux Grid Capacitor"},{"containers":[{"description":"Dedicated Oathkeeper instance enforcing per-scope access rules for Flexa API. Introspects OAuth2 tokens requiring flexa:<scope> scope before proxying.","documentation":{},"id":"54","name":"Flexa API Oathkeeper","properties":{"structurizr.dsl.identifier":"FlexaGatewaySystem.FlexaOathkeeper"},"relationships":[{"description":"forwards requests to","destinationId":"56","id":"58","sourceId":"54","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"sends sync-features webhook after deployment","destinationId":"62","id":"243","properties":{"structurizr.dsl.identifier":"flexaBackendRelationships"},"sourceId":"54","tags":"Relationship","technology":"REST/JSON"},{"description":"sends sync-features webhook after deployment","destinationId":"59","id":"244","linkedRelationshipId":"243","sourceId":"54","technology":"REST/JSON"},{"description":"validates Flexa OAuth2 clients via","destinationId":"39","id":"247","properties":{"structurizr.dsl.identifier":"flexaAuthHydraRelationships"},"sourceId":"54","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"validates Flexa OAuth2 clients via","destinationId":"34","id":"248","linkedRelationshipId":"247","sourceId":"54","technology":"JSON/HTTPS"}],"tags":"Element,Container,Service","technology":"ORY Oathkeeper"},{"description":"CI/CD tool reading gateway-config.json to generate per-scope Oathkeeper access rules and post-deploy-payload.json. Triggers feature sync webhook to Service UI after deployment.","documentation":{},"id":"55","name":"Access Rules Generator","properties":{"structurizr.dsl.identifier":"FlexaGatewaySystem.AccessRulesGenerator"},"relationships":[{"description":"deploys generated access rules to","destinationId":"54","id":"57","sourceId":"55","tags":"Relationship","technology":"Helm/K8s"}],"tags":"Element,Container,Service","technology":"Node.js"},{"description":"Flexa Service","documentation":{},"id":"56","name":"Flexa Service","properties":{"structurizr.dsl.identifier":"FlexaGatewaySystem.FlexaService"},"tags":"Element,Container,Service"}],"description":"Manages routing rules and scope-based access control for the Flexa API. CI generates Oathkeeper access rules and syncs available Flexa features to Service UI after deployment.","documentation":{},"group":"Flexa API Gateway","id":"53","name":"Flexa Gateway Config","properties":{"structurizr.dsl.identifier":"FlexaGatewaySystem"},"relationships":[{"description":"sends sync-features webhook after deployment","destinationId":"62","id":"245","linkedRelationshipId":"243","sourceId":"53","technology":"REST/JSON"},{"description":"sends sync-features webhook after deployment","destinationId":"59","id":"246","linkedRelationshipId":"243","sourceId":"53","technology":"REST/JSON"},{"description":"validates Flexa OAuth2 clients via","destinationId":"39","id":"249","linkedRelationshipId":"247","sourceId":"53","technology":"JSON/HTTPS"},{"description":"validates Flexa OAuth2 clients via","destinationId":"34","id":"250","linkedRelationshipId":"247","sourceId":"53","technology":"JSON/HTTPS"}],"tags":"Element,Software System,Flexa Gateway Config"},{"containers":[{"description":"Main Application for managing the organizations, users, logs, projects, devices and modules","documentation":{},"id":"60","name":"Service UI Web Portal","properties":{"structurizr.dsl.identifier":"ServiceUISystem.ServiceUI"},"relationships":[{"description":"interacts with API Gateway","destinationId":"72","id":"90","properties":{"structurizr.dsl.identifier":"serviceUIRelationships"},"sourceId":"60","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Container,WebApp","technology":"React"},{"description":"Allow users to self claim their devices to the company","documentation":{},"id":"61","name":"Self Claiming UI","properties":{"structurizr.dsl.identifier":"ServiceUISystem.SelfClaimingUI"},"relationships":[{"description":"interacts with API Gateway","destinationId":"72","id":"89","properties":{"structurizr.dsl.identifier":"selfClaimingUIBackendRelationships"},"sourceId":"61","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Container,WebApp","technology":"React"},{"components":[{"description":"Responsible for company management","documentation":{},"id":"63","name":"Company Module","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Backend.Company"},"relationships":[{"description":"reads and writes to","destinationId":"87","id":"136","properties":{"structurizr.dsl.identifier":"companyDBRelationships"},"sourceId":"63","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"137","linkedRelationshipId":"136","sourceId":"63","technology":"SQL/TCP"}],"tags":"Element,Component","technology":"Nest.js"},{"description":"Responsible for user management","documentation":{},"id":"64","name":"Users Module","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Backend.Users"},"relationships":[{"description":"reads and writes to","destinationId":"80","id":"117","properties":{"structurizr.dsl.identifier":"usersKratosIdentityRelationships"},"sourceId":"64","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads and writes to","destinationId":"78","id":"118","linkedRelationshipId":"117","sourceId":"64","technology":"JSON/HTTPS"},{"description":"reads from","destinationId":"82","id":"121","properties":{"structurizr.dsl.identifier":"usersKetoRelationshipsRead"},"sourceId":"64","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads from","destinationId":"81","id":"122","linkedRelationshipId":"121","sourceId":"64","technology":"JSON/HTTPS"},{"description":"writes to","destinationId":"83","id":"125","properties":{"structurizr.dsl.identifier":"usersKetoRelationshipsWrite"},"sourceId":"64","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Component","technology":"Nest.js"},{"description":"Responsible for handling dynamic ES simulation data","documentation":{},"id":"65","name":"Dynamic ES Module","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Backend.DynamicES"},"relationships":[{"description":"reads and writes to","destinationId":"87","id":"132","properties":{"structurizr.dsl.identifier":"dynamicESDBRelationships"},"sourceId":"65","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"133","linkedRelationshipId":"132","sourceId":"65","technology":"SQL/TCP"},{"description":"retrieves simulation data from","destinationId":"9","id":"187","sourceId":"65","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads and writes data to","destinationId":"24","id":"194","properties":{"structurizr.dsl.identifier":"dynamicESObjectStorageRelationships"},"sourceId":"65","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads and writes data to","destinationId":"23","id":"195","linkedRelationshipId":"194","sourceId":"65","technology":"JSON/HTTPS"}],"tags":"Element,Component","technology":"Nest.js"},{"description":"Responsible for handling audit logs","documentation":{},"id":"66","name":"Audit Logs Module","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Backend.AuditLogs"},"relationships":[{"description":"reads and writes to","destinationId":"87","id":"140","properties":{"structurizr.dsl.identifier":"auditLogsDBRelationships"},"sourceId":"66","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"141","linkedRelationshipId":"140","sourceId":"66","technology":"SQL/TCP"}],"tags":"Element,Component","technology":"Nest.js"},{"description":"Responsible for project management","documentation":{},"id":"67","name":"Projects Module","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Backend.Projects"},"relationships":[{"description":"reads and writes to","destinationId":"87","id":"138","properties":{"structurizr.dsl.identifier":"projectsDBRelationships"},"sourceId":"67","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"139","linkedRelationshipId":"138","sourceId":"67","technology":"SQL/TCP"}],"tags":"Element,Component","technology":"Nest.js"},{"description":"[Session, Access Control] Responsible for handling access control to resources","documentation":{},"id":"68","name":"API Module","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Backend.Api"},"relationships":[{"description":"get user session from","destinationId":"80","id":"127","properties":{"structurizr.dsl.identifier":"apiKratosRelationships"},"sourceId":"68","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"get user session from","destinationId":"78","id":"128","linkedRelationshipId":"127","sourceId":"68","technology":"JSON/HTTPS"}],"tags":"Element,Component","technology":"Nest.js"},{"description":"Responsible for handling energy monitoring configuration","documentation":{},"id":"69","name":"Energy Monitoring Module","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Backend.EnergyMonitoring"},"relationships":[{"description":"reads and writes to","destinationId":"87","id":"154","properties":{"structurizr.dsl.identifier":"energyMonitoringDBRelationships"},"sourceId":"69","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"155","linkedRelationshipId":"154","sourceId":"69","technology":"SQL/TCP"},{"description":"fetches gateway devices from","destinationId":"52","id":"156","properties":{"structurizr.dsl.identifier":"energyMonitoringFluxCapacitorRelationships"},"sourceId":"69","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"fetches gateway devices from","destinationId":"50","id":"157","linkedRelationshipId":"156","sourceId":"69","technology":"JSON/HTTPS"},{"description":"fetches gateway devices from","destinationId":"49","id":"158","linkedRelationshipId":"156","sourceId":"69","technology":"JSON/HTTPS"},{"description":"synchronizes configuration with","destinationId":"27","id":"209","properties":{"structurizr.dsl.identifier":"serviceUISystemBackendEnergyMonitoringRelationships"},"sourceId":"69","tags":"Relationship","technology":"REST/JSON"},{"description":"synchronizes configuration with","destinationId":"25","id":"210","linkedRelationshipId":"209","sourceId":"69","technology":"REST/JSON"},{"description":"registers new OAuth2.0 applications with","destinationId":"39","id":"228","properties":{"structurizr.dsl.identifier":"serviceUISystemBackendCentralAuthHydraRelationships"},"sourceId":"69","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"registers new OAuth2.0 applications with","destinationId":"34","id":"229","linkedRelationshipId":"228","sourceId":"69","technology":"JSON/HTTPS"},{"description":"sends user invitation webhooks to","destinationId":"37","id":"234","properties":{"structurizr.dsl.identifier":"serviceUISystemBackendOauthBackendWebhookRelationships"},"sourceId":"69","tags":"Relationship","technology":"REST/JSON"}],"tags":"Element,Component","technology":"Nest.js"},{"description":"Manages Flexa OAuth2 client lifecycle, scope-based API access credentials, and feature synchronization via incoming webhooks","documentation":{},"id":"70","name":"Flexa Module","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Backend.Flexa"},"relationships":[{"description":"reads and writes to","destinationId":"87","id":"162","properties":{"structurizr.dsl.identifier":"flexaDBRelationships"},"sourceId":"70","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"163","linkedRelationshipId":"162","sourceId":"70","technology":"SQL/TCP"},{"description":"reads from","destinationId":"82","id":"164","properties":{"structurizr.dsl.identifier":"flexaKetoReadRelationships"},"sourceId":"70","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads from","destinationId":"81","id":"165","linkedRelationshipId":"164","sourceId":"70","technology":"JSON/HTTPS"},{"description":"writes to","destinationId":"83","id":"166","properties":{"structurizr.dsl.identifier":"flexaKetoWriteRelationships"},"sourceId":"70","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"manages Flexa OAuth2 clients via","destinationId":"39","id":"251","properties":{"structurizr.dsl.identifier":"flexaBackendCentralAuthHydraRelationships"},"sourceId":"70","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"manages Flexa OAuth2 clients via","destinationId":"34","id":"252","linkedRelationshipId":"251","sourceId":"70","technology":"JSON/HTTPS"}],"tags":"Element,Component","technology":"Nest.js"}],"description":"Responsible for handling service UI business logic","documentation":{},"id":"62","name":"Service UI Backend","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Backend"},"relationships":[{"description":"reads and writes to","destinationId":"80","id":"119","linkedRelationshipId":"117","sourceId":"62","technology":"JSON/HTTPS"},{"description":"reads and writes to","destinationId":"78","id":"120","linkedRelationshipId":"117","sourceId":"62","technology":"JSON/HTTPS"},{"description":"reads from","destinationId":"82","id":"123","linkedRelationshipId":"121","sourceId":"62","technology":"JSON/HTTPS"},{"description":"reads from","destinationId":"81","id":"124","linkedRelationshipId":"121","sourceId":"62","technology":"JSON/HTTPS"},{"description":"writes to","destinationId":"83","id":"126","linkedRelationshipId":"125","sourceId":"62","technology":"JSON/HTTPS"},{"description":"reads and writes to","destinationId":"87","id":"134","linkedRelationshipId":"132","sourceId":"62","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"135","linkedRelationshipId":"132","sourceId":"62","technology":"SQL/TCP"},{"description":"fetches gateway devices from","destinationId":"52","id":"159","linkedRelationshipId":"156","sourceId":"62","technology":"JSON/HTTPS"},{"description":"fetches gateway devices from","destinationId":"50","id":"160","linkedRelationshipId":"156","sourceId":"62","technology":"JSON/HTTPS"},{"description":"fetches gateway devices from","destinationId":"49","id":"161","linkedRelationshipId":"156","sourceId":"62","technology":"JSON/HTTPS"},{"description":"retrieves simulation data from","destinationId":"9","id":"188","linkedRelationshipId":"187","sourceId":"62","technology":"JSON/HTTPS"},{"description":"reads and writes data to","destinationId":"24","id":"196","linkedRelationshipId":"194","sourceId":"62","technology":"JSON/HTTPS"},{"description":"reads and writes data to","destinationId":"23","id":"197","linkedRelationshipId":"194","sourceId":"62","technology":"JSON/HTTPS"},{"description":"synchronizes configuration with","destinationId":"27","id":"211","linkedRelationshipId":"209","sourceId":"62","technology":"REST/JSON"},{"description":"synchronizes configuration with","destinationId":"25","id":"212","linkedRelationshipId":"209","sourceId":"62","technology":"REST/JSON"},{"description":"registers new OAuth2.0 applications with","destinationId":"39","id":"230","linkedRelationshipId":"228","sourceId":"62","technology":"JSON/HTTPS"},{"description":"registers new OAuth2.0 applications with","destinationId":"34","id":"231","linkedRelationshipId":"228","sourceId":"62","technology":"JSON/HTTPS"},{"description":"sends user invitation webhooks to","destinationId":"37","id":"235","linkedRelationshipId":"234","sourceId":"62","technology":"REST/JSON"}],"tags":"Element,Container,Service","technology":"Nest.js"},{"description":"Used for storing self claiming code_challenge by authenticated devices","documentation":{},"id":"71","name":"Nginx Auth Proxy for Self Claiming","properties":{"structurizr.dsl.identifier":"ServiceUISystem.NginxAuthProxy"},"relationships":[{"description":"stores code_challenge in","destinationId":"52","id":"129","properties":{"structurizr.dsl.identifier":"nginxAuthProxyFluxCapacitorRelationships"},"sourceId":"71","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"stores code_challenge in","destinationId":"50","id":"130","linkedRelationshipId":"129","sourceId":"71","technology":"JSON/HTTPS"},{"description":"stores code_challenge in","destinationId":"49","id":"131","linkedRelationshipId":"129","sourceId":"71","technology":"JSON/HTTPS"}],"tags":"Element,Container,Service","technology":"NGINX"},{"components":[{"description":"Routes to Kratos identity APIs.","documentation":{},"id":"73","name":"Access Rules to ORY Kratos","properties":{"structurizr.dsl.identifier":"ServiceUISystem.OAuthkeeper.Kratos"},"relationships":[{"description":"authenticates users","destinationId":"79","id":"91","properties":{"structurizr.dsl.identifier":"oauthFrontendRelationships"},"sourceId":"73","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"authenticates users","destinationId":"78","id":"92","linkedRelationshipId":"91","sourceId":"73","technology":"JSON/HTTPS"}],"tags":"Element,Component,Service","technology":"ORY Oathkeeper"},{"description":"Routes to Keto permission APIs.","documentation":{},"id":"74","name":"Access Rules to ORY Keto","properties":{"structurizr.dsl.identifier":"ServiceUISystem.OAuthkeeper.Keto"},"relationships":[{"description":"reads relationships","destinationId":"82","id":"95","properties":{"structurizr.dsl.identifier":"oauthKetoRelationshipsRead"},"sourceId":"74","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads relationships","destinationId":"81","id":"96","linkedRelationshipId":"95","sourceId":"74","technology":"JSON/HTTPS"},{"description":"writes relationships","destinationId":"83","id":"99","properties":{"structurizr.dsl.identifier":"oauthKetoRelationshipsWrite"},"sourceId":"74","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Component,Service","technology":"ORY Oathkeeper"},{"description":"Routes to Service UI backend.","documentation":{},"id":"75","name":"Access Rules to Service UI backend","properties":{"structurizr.dsl.identifier":"ServiceUISystem.OAuthkeeper.Backend"},"relationships":[{"description":"business logic","destinationId":"62","id":"101","properties":{"structurizr.dsl.identifier":"oauthBackendRelationships"},"sourceId":"75","tags":"Relationship","technology":"JSON/HTTPS"}],"tags":"Element,Component,Service","technology":"ORY Oathkeeper"},{"description":"Routes to Flux Capacitor GraphQL.","documentation":{},"id":"76","name":"Access Rules to Flux Capacitor GraphQL","properties":{"structurizr.dsl.identifier":"ServiceUISystem.OAuthkeeper.GraphQL"},"relationships":[{"description":"reads device data from","destinationId":"51","id":"103","properties":{"structurizr.dsl.identifier":"oauthGraphQLRelationships"},"sourceId":"76","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"50","id":"104","linkedRelationshipId":"103","sourceId":"76","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"49","id":"105","linkedRelationshipId":"103","sourceId":"76","technology":"JSON/HTTPS"}],"tags":"Element,Component,Service","technology":"ORY Oathkeeper"},{"description":"Routes to Flux Capacitor REST API.","documentation":{},"id":"77","name":"Access Rules to Flux Capacitor Rest API","properties":{"structurizr.dsl.identifier":"ServiceUISystem.OAuthkeeper.FluxGrid"},"relationships":[{"description":"reads and writes device data to","destinationId":"52","id":"112","properties":{"structurizr.dsl.identifier":"oauthFluxGridRelationships"},"sourceId":"77","tags":"Relationship","technology":"JSON/HTTPS"},{"description":"reads and writes device data to","destinationId":"50","id":"113","linkedRelationshipId":"112","sourceId":"77","technology":"JSON/HTTPS"},{"description":"reads and writes device data to","destinationId":"49","id":"114","linkedRelationshipId":"112","sourceId":"77","technology":"JSON/HTTPS"}],"tags":"Element,Component,Service","technology":"ORY Oathkeeper"}],"description":"API gateway for auth and access rules.","documentation":{},"id":"72","name":"OAuthkeeper API Gateway","properties":{"structurizr.dsl.identifier":"ServiceUISystem.OAuthkeeper"},"relationships":[{"description":"authenticates users","destinationId":"79","id":"93","linkedRelationshipId":"91","sourceId":"72","technology":"JSON/HTTPS"},{"description":"authenticates users","destinationId":"78","id":"94","linkedRelationshipId":"91","sourceId":"72","technology":"JSON/HTTPS"},{"description":"reads relationships","destinationId":"82","id":"97","linkedRelationshipId":"95","sourceId":"72","technology":"JSON/HTTPS"},{"description":"reads relationships","destinationId":"81","id":"98","linkedRelationshipId":"95","sourceId":"72","technology":"JSON/HTTPS"},{"description":"writes relationships","destinationId":"83","id":"100","linkedRelationshipId":"99","sourceId":"72","technology":"JSON/HTTPS"},{"description":"business logic","destinationId":"62","id":"102","linkedRelationshipId":"101","sourceId":"72","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"51","id":"106","linkedRelationshipId":"103","sourceId":"72","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"50","id":"107","linkedRelationshipId":"103","sourceId":"72","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"49","id":"108","linkedRelationshipId":"103","sourceId":"72","technology":"JSON/HTTPS"},{"description":"reads and writes device data to","destinationId":"52","id":"115","linkedRelationshipId":"112","sourceId":"72","technology":"JSON/HTTPS"}],"tags":"Element,Container,Service","technology":"ORY Oathkeeper"},{"components":[{"description":"Endpoints used by frontend applications to manage a user's own profile.","documentation":{},"id":"79","name":"Kratos Frontend API","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Kratos.FrontendAPI"},"relationships":[{"description":"reads and writes to","destinationId":"85","id":"146","properties":{"structurizr.dsl.identifier":"kratosFrontendDBRelationships"},"sourceId":"79","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"147","linkedRelationshipId":"146","sourceId":"79","technology":"SQL/TCP"}],"tags":"Element,Component","technology":"Golang"},{"description":"APIs for managing identities.","documentation":{},"id":"80","name":"Kratos Identity API","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Kratos.IdentityAPI"},"relationships":[{"description":"reads and writes to","destinationId":"85","id":"142","properties":{"structurizr.dsl.identifier":"kratosIdentityDBRelationships"},"sourceId":"80","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"143","linkedRelationshipId":"142","sourceId":"80","technology":"SQL/TCP"}],"tags":"Element,Component","technology":"Golang"}],"description":"Identity and user management.","documentation":{},"id":"78","name":"Kratos","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Kratos"},"relationships":[{"description":"reads and writes to","destinationId":"85","id":"144","linkedRelationshipId":"142","sourceId":"78","technology":"SQL/TCP"},{"description":"reads and writes to","destinationId":"84","id":"145","linkedRelationshipId":"142","sourceId":"78","technology":"SQL/TCP"}],"tags":"Element,Container,Service","technology":"ORY Kratos"},{"components":[{"description":"Reads permission relationships.","documentation":{},"id":"82","name":"Keto Relationships Read API","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Keto.KetoRelationshipsReadAPI"},"relationships":[{"description":"reads from","destinationId":"86","id":"148","properties":{"structurizr.dsl.identifier":"ketoDBRelationships"},"sourceId":"82","tags":"Relationship","technology":"SQL/TCP"},{"description":"reads from","destinationId":"84","id":"149","linkedRelationshipId":"148","sourceId":"82","technology":"SQL/TCP"}],"tags":"Element,Component","technology":"Golang"},{"description":"Writes permission relationships.","documentation":{},"id":"83","name":"Keto Relationships Write API","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Keto.KetoRelationshipsWriteAPI"},"relationships":[{"description":"writes to","destinationId":"86","id":"152","properties":{"structurizr.dsl.identifier":"ketoDBRelationshipsWrite"},"sourceId":"83","tags":"Relationship","technology":"SQL/TCP"},{"description":"writes to","destinationId":"84","id":"153","linkedRelationshipId":"152","sourceId":"83","technology":"SQL/TCP"}],"tags":"Element,Component","technology":"Golang"}],"description":"Permission and relationship store.","documentation":{},"id":"81","name":"Keto","properties":{"structurizr.dsl.identifier":"ServiceUISystem.Keto"},"relationships":[{"description":"reads from","destinationId":"86","id":"150","linkedRelationshipId":"148","sourceId":"81","technology":"SQL/TCP"},{"description":"reads from","destinationId":"84","id":"151","linkedRelationshipId":"148","sourceId":"81","technology":"SQL/TCP"}],"tags":"Element,Container,Service","technology":"ORY Keto"},{"components":[{"description":"Identity data for Kratos.","documentation":{},"id":"85","name":"Kratos Database","properties":{"structurizr.dsl.identifier":"ServiceUISystem.PostgreSql.KratosDatabase"},"tags":"Element,Component,Database","technology":"PostgreSql"},{"description":"Permission data for Keto.","documentation":{},"id":"86","name":"Keto Database","properties":{"structurizr.dsl.identifier":"ServiceUISystem.PostgreSql.KetoDatabase"},"tags":"Element,Component,Database","technology":"PostgreSql"},{"description":"Service UI business data.","documentation":{},"id":"87","name":"Service UI Database","properties":{"structurizr.dsl.identifier":"ServiceUISystem.PostgreSql.ServiceUIDatabase"},"tags":"Element,Component,Database","technology":"PostgreSql"},{"description":"Flux Capacitor device data.","documentation":{},"id":"88","name":"Flux Grid Database","properties":{"structurizr.dsl.identifier":"ServiceUISystem.PostgreSql.FluxGridDatabase"},"tags":"Element,Component,Database","technology":"PostgreSql"}],"description":"Shared PostgreSQL instance for Service UI data.","documentation":{},"id":"84","name":"PostgreSql","properties":{"structurizr.dsl.identifier":"ServiceUISystem.PostgreSql"},"tags":"Element,Container,Database","technology":"PostgreSql"}],"description":"Main portal for organizations, users, projects, devices and modules.","documentation":{},"group":"Service UI","id":"59","name":"Service UI","properties":{"structurizr.dsl.identifier":"ServiceUISystem"},"relationships":[{"description":"reads device data from","destinationId":"51","id":"109","linkedRelationshipId":"103","sourceId":"59","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"50","id":"110","linkedRelationshipId":"103","sourceId":"59","technology":"JSON/HTTPS"},{"description":"reads device data from","destinationId":"49","id":"111","linkedRelationshipId":"103","sourceId":"59","technology":"JSON/HTTPS"},{"description":"reads and writes device data to","destinationId":"52","id":"116","linkedRelationshipId":"112","sourceId":"59","technology":"JSON/HTTPS"},{"description":"retrieves simulation data from","destinationId":"9","id":"189","linkedRelationshipId":"187","sourceId":"59","technology":"JSON/HTTPS"},{"description":"reads and writes data to","destinationId":"24","id":"198","linkedRelationshipId":"194","sourceId":"59","technology":"JSON/HTTPS"},{"description":"reads and writes data to","destinationId":"23","id":"199","linkedRelationshipId":"194","sourceId":"59","technology":"JSON/HTTPS"},{"description":"synchronizes configuration with","destinationId":"27","id":"213","linkedRelationshipId":"209","sourceId":"59","technology":"REST/JSON"},{"description":"synchronizes configuration with","destinationId":"25","id":"214","linkedRelationshipId":"209","sourceId":"59","technology":"REST/JSON"},{"description":"registers new OAuth2.0 applications with","destinationId":"39","id":"232","linkedRelationshipId":"228","sourceId":"59","technology":"JSON/HTTPS"},{"description":"registers new OAuth2.0 applications with","destinationId":"34","id":"233","linkedRelationshipId":"228","sourceId":"59","technology":"JSON/HTTPS"},{"description":"sends user invitation webhooks to","destinationId":"37","id":"236","linkedRelationshipId":"234","sourceId":"59","technology":"REST/JSON"}],"tags":"Element,Software System,Service UI"}]},"name":"Service UI Architecture","properties":{"structurizr.inspection.info":"0","structurizr.inspection.ignore":"0","structurizr.inspection.error":"21","structurizr.inspection.warning":"0"},"views":{"componentViews":[{"containerId":"18","dimensions":{"height":1503,"width":1598},"elements":[{"id":"19","x":539,"y":765},{"id":"59","x":540,"y":160}],"externalContainerBoundariesVisible":false,"key":"Components_HEMS_App","name":"Component View: Home Energy Management System - Consolinno HEMS Mobile App","order":5,"relationships":[{"id":"201"}]},{"containerId":"27","dimensions":{"height":1498,"width":1685},"elements":[{"id":"28","x":239,"y":200},{"id":"29","x":994,"y":200},{"id":"30","x":243,"y":859},{"id":"49","x":1004,"y":855}],"externalContainerBoundariesVisible":false,"key":"Components_EnergyMonitoring_Backend","name":"Component View: Energy Monitoring - Energy Monitoring Backend","order":6,"relationships":[{"id":"217"},{"id":"32"}]},{"containerId":"62","dimensions":{"height":2855,"width":4735},"elements":[{"id":"9","x":3545,"y":160},{"id":"23","x":2999,"y":159},{"id":"25","x":4085,"y":160},{"id":"34","x":2475,"y":2300},{"id":"49","x":3830,"y":2314},{"id":"63","x":1770,"y":764},{"id":"64","x":755,"y":764},{"id":"65","x":3290,"y":759},{"id":"66","x":2785,"y":759},{"id":"67","x":2275,"y":764},{"id":"68","x":240,"y":764},{"id":"69","x":3810,"y":764},{"id":"70","x":1260,"y":769},{"id":"78","x":480,"y":1744},{"id":"81","x":1065,"y":1744},{"id":"84","x":2465,"y":1704}],"externalContainerBoundariesVisible":false,"key":"Components_ServiceUI","name":"Component View: Service UI - Service UI Backend","order":11,"relationships":[{"id":"118"},{"id":"122"},{"id":"128"},{"id":"133"},{"id":"137"},{"id":"139"},{"id":"141"},{"id":"155"},{"id":"158"},{"id":"163"},{"id":"165"},{"id":"182"},{"id":"187"},{"id":"195"},{"id":"210"},{"id":"229","vertices":[{"x":3575,"y":1775}]},{"id":"240","vertices":[{"x":3330,"y":1665}]},{"id":"252"}]},{"containerId":"72","dimensions":{"height":1915,"width":3930},"elements":[{"id":"49","x":2880,"y":1374},{"id":"62","x":1740,"y":799},{"id":"73","x":240,"y":199},{"id":"74","x":990,"y":199},{"id":"75","x":1740,"y":199},{"id":"76","x":2490,"y":199},{"id":"77","x":3240,"y":199},{"id":"78","x":240,"y":799},{"id":"81","x":990,"y":799}],"externalContainerBoundariesVisible":false,"key":"Components_OAuthkeeper","name":"Component View: Service UI - OAuthkeeper API Gateway","order":12,"relationships":[{"id":"101"},{"id":"105"},{"id":"114"},{"id":"92"},{"id":"96"}]},{"containerId":"78","dimensions":{"height":1933,"width":1940},"elements":[{"id":"62","x":200,"y":165},{"id":"72","x":994,"y":165},{"id":"79","x":1270,"y":790},{"id":"80","x":608,"y":798},{"id":"84","x":608,"y":1398}],"externalContainerBoundariesVisible":false,"key":"Components_Kratos","name":"Component View: Service UI - Kratos","order":13,"relationships":[{"id":"119"},{"id":"135","vertices":[{"x":420,"y":1030}]},{"id":"143"},{"id":"147"},{"id":"93"}]},{"containerId":"81","dimensions":{"height":1910,"width":1779},"elements":[{"id":"62","x":990,"y":165},{"id":"72","x":205,"y":165},{"id":"82","x":219,"y":772},{"id":"83","x":975,"y":760},{"id":"84","x":600,"y":1375}],"externalContainerBoundariesVisible":false,"key":"Components_Keto","name":"Component View: Service UI - Keto","order":14,"relationships":[{"id":"100","vertices":[{"x":785,"y":315}]},{"id":"123","vertices":[{"x":855,"y":325}]},{"id":"126"},{"id":"135","vertices":[{"x":1505,"y":320},{"x":1510,"y":1350}]},{"id":"149"},{"id":"153"},{"id":"97"}]},{"containerId":"50","dimensions":{"height":2200,"width":1680},"elements":[{"id":"10","x":614,"y":1660},{"id":"25","x":984,"y":160},{"id":"51","x":990,"y":928},{"id":"52","x":240,"y":928},{"id":"59","x":304,"y":160}],"externalContainerBoundariesVisible":false,"key":"Components_FluxCapacitor","name":"Component View: Flux Grid Capacitor - Flux Grid Capacitor","order":15,"relationships":[{"id":"109","vertices":[{"x":979,"y":660}]},{"id":"116","vertices":[{"x":349,"y":685}]},{"id":"168"},{"id":"174"},{"id":"177","vertices":[{"x":554,"y":710}]},{"id":"186","vertices":[{"x":749,"y":670}]},{"id":"221"}]},{"containerId":"84","dimensions":{"height":1938,"width":3180},"elements":[{"id":"49","x":996,"y":160},{"id":"62","x":240,"y":600},{"id":"78","x":1740,"y":600},{"id":"81","x":2490,"y":600},{"id":"85","x":1740,"y":1200},{"id":"86","x":2490,"y":1200},{"id":"87","x":240,"y":1200},{"id":"88","x":990,"y":1200}],"externalContainerBoundariesVisible":false,"key":"Components_PostgreSql","name":"Component View: Service UI - PostgreSql","order":16,"relationships":[{"id":"134"},{"id":"144"},{"id":"150"},{"id":"181"}]}],"configuration":{"branding":{},"lastSavedView":"Containers_ServiceUI","metadataSymbols":"SquareBrackets","styles":{"elements":[{"background":"#1268bd","color":"#ffffff","tag":"Central Auth"},{"background":"#85bbf0","color":"#000000","tag":"Component"},{"background":"#438dd5","color":"#ffffff","shape":"RoundedBox","tag":"Container"},{"shape":"Cylinder","tag":"Database"},{"background":"#555555","color":"#ffffff","tag":"External"},{"background":"#1268bd","color":"#ffffff","tag":"Flexa Gateway Config"},{"background":"#1268bd","color":"#ffffff","tag":"Flux Grid Capacitor"},{"background":"#7825fc","tag":"Future"},{"color":"#000000","shape":"RoundedBox","tag":"Group"},{"background":"#0d9534","color":"#000000","tag":"Hardware"},{"background":"#555555","color":"#ffffff","tag":"Internal"},{"shape":"MobileDevicePortrait","tag":"MobileApp"},{"background":"#07427a","color":"#ffffff","fontSize":25,"shape":"Person","tag":"Person"},{"background":"#1268bd","color":"#ffffff","tag":"Software System"},{"shape":"Cylinder","tag":"Store"},{"shape":"WebBrowser","tag":"WebApp"}],"relationships":[{"color":"#7825fc","tag":"Future"},{"fontSize":30,"tag":"Relationship"}]},"terminology":{}},"containerViews":[{"dimensions":{"height":1399,"width":893},"elements":[{"id":"24","x":221,"y":760},{"id":"59","x":221,"y":160}],"externalSoftwareSystemBoundariesVisible":false,"key":"Containers_SysEleven","name":"Container View: SysEleven","order":4,"relationships":[{"id":"198"}],"softwareSystemId":"23"},{"dimensions":{"height":2778,"width":1651},"elements":[{"id":"8","x":580,"y":159},{"id":"26","x":219,"y":933},{"id":"27","x":225,"y":1514},{"id":"30","x":234,"y":2138},{"id":"34","x":975,"y":914},{"id":"49","x":1000,"y":2119},{"id":"59","x":995,"y":1499}],"externalSoftwareSystemBoundariesVisible":false,"key":"Containers_EnergyMonitoring","name":"Container View: Energy Monitoring","order":9,"relationships":[{"id":"213"},{"id":"220"},{"id":"225"},{"id":"264"},{"id":"267"},{"id":"31"},{"id":"33"}],"softwareSystemId":"25"},{"dimensions":{"height":3832,"width":3604},"elements":[{"id":"5","x":1795,"y":160},{"id":"7","x":1330,"y":160},{"id":"9","x":2725,"y":2296},{"id":"16","x":355,"y":295},{"id":"23","x":2730,"y":1936},{"id":"25","x":2725,"y":2661},{"id":"34","x":2730,"y":1186},{"id":"49","x":2730,"y":3026},{"id":"53","x":2735,"y":1571},{"id":"60","x":1770,"y":885},{"id":"61","x":1035,"y":890},{"id":"62","x":1765,"y":2195},{"id":"71","x":345,"y":890},{"id":"72","x":1770,"y":1500},{"id":"78","x":610,"y":2195},{"id":"81","x":595,"y":1496},{"id":"84","x":1770,"y":3020}],"externalSoftwareSystemBoundariesVisible":false,"key":"Containers_ServiceUI","name":"Container View: Service UI","order":10,"relationships":[{"id":"102"},{"id":"108","vertices":[{"x":2375,"y":1036},{"x":3315,"y":1036},{"x":3320,"y":3186}]},{"id":"120"},{"id":"124","vertices":[{"x":1465,"y":1821}]},{"id":"131","vertices":[{"x":225,"y":1030},{"x":230,"y":3540},{"x":2955,"y":3526}]},{"id":"135"},{"id":"145"},{"id":"151","vertices":[{"x":535,"y":1660},{"x":535,"y":3160}]},{"id":"161","vertices":[{"x":2505,"y":3031}]},{"id":"182"},{"id":"188","vertices":[{"x":2570,"y":2481}]},{"id":"197"},{"id":"204"},{"id":"208"},{"id":"212","vertices":[{"x":2610,"y":2801}]},{"id":"231","vertices":[{"x":2640,"y":1506}]},{"id":"241","vertices":[{"x":2475,"y":1331}]},{"id":"245","vertices":[{"x":2635,"y":1886}]},{"id":"253"},{"id":"259"},{"id":"261"},{"id":"89"},{"id":"90"},{"id":"94","vertices":[{"x":1820,"y":2096}]},{"id":"98"}],"softwareSystemId":"59"},{"dimensions":{"height":2924,"width":2540},"elements":[{"id":"1","x":1889,"y":2384},{"id":"2","x":1874,"y":1279},{"id":"3","x":1879,"y":1649},{"id":"4","x":1884,"y":2009},{"id":"6","x":1720,"y":160},{"id":"7","x":1100,"y":160},{"id":"10","x":199,"y":2021},{"id":"17","x":979,"y":2014},{"id":"18","x":1479,"y":839},{"id":"20","x":964,"y":1494},{"id":"59","x":254,"y":901}],"externalSoftwareSystemBoundariesVisible":false,"key":"Containers_HEMS","name":"Container View: Home Energy Management System","order":18,"relationships":[{"id":"191"},{"id":"203"},{"id":"21"},{"id":"22"},{"id":"257"},{"id":"262"},{"id":"268"},{"id":"270"},{"id":"272"},{"id":"274"}],"softwareSystemId":"16"},{"dimensions":{"height":1434,"width":2425},"elements":[{"id":"11","x":1754,"y":795},{"id":"12","x":959,"y":795},{"id":"13","x":219,"y":790},{"id":"16","x":965,"y":160},{"id":"49","x":1744,"y":160}],"externalSoftwareSystemBoundariesVisible":false,"key":"Containers_Mender","name":"Container View: Mender IO","order":20,"relationships":[{"id":"14"},{"id":"15"},{"id":"171"},{"id":"192"}],"softwareSystemId":"10"},{"dimensions":{"height":3824,"width":2854},"elements":[{"id":"8","x":1490,"y":160},{"id":"25","x":2154,"y":1000},{"id":"35","x":814,"y":977},{"id":"36","x":810,"y":1570},{"id":"37","x":1339,"y":2192},{"id":"38","x":219,"y":2195},{"id":"39","x":804,"y":2570},{"id":"40","x":785,"y":3185},{"id":"53","x":2150,"y":3005},{"id":"59","x":2159,"y":2220}],"externalSoftwareSystemBoundariesVisible":false,"key":"Containers_CentralAuth","name":"Container View: Central Auth","order":22,"relationships":[{"id":"214"},{"id":"226"},{"id":"232","vertices":[{"x":2379,"y":2740}]},{"id":"236","vertices":[{"x":1974,"y":2475}]},{"id":"239","vertices":[{"x":1964,"y":2270}]},{"id":"246","vertices":[{"x":2570,"y":2790}]},{"id":"249"},{"id":"265"},{"id":"266"},{"id":"41"},{"id":"42"},{"id":"43"},{"id":"44"},{"id":"45"},{"id":"46"},{"id":"47"},{"id":"48"}],"softwareSystemId":"34"},{"dimensions":{"height":2019,"width":1631},"elements":[{"id":"34","x":981,"y":530},{"id":"54","x":220,"y":779},{"id":"55","x":220,"y":179},{"id":"56","x":220,"y":1379},{"id":"59","x":976,"y":1080}],"externalSoftwareSystemBoundariesVisible":false,"key":"Containers_FlexaGateway","name":"Container View: Flexa Gateway Config","order":24,"relationships":[{"id":"244"},{"id":"248"},{"id":"57"},{"id":"58"}],"softwareSystemId":"53"},{"dimensions":{"height":1976,"width":1413},"elements":[{"id":"10","x":745,"y":1435},{"id":"25","x":200,"y":160},{"id":"50","x":743,"y":812},{"id":"59","x":740,"y":159}],"externalSoftwareSystemBoundariesVisible":false,"key":"Containers_FluxCapacitor","name":"Container View: Flux Grid Capacitor","order":26,"relationships":[{"id":"110","vertices":[{"x":835,"y":639}]},{"id":"170"},{"id":"180","vertices":[{"x":1105,"y":619}]},{"id":"222","vertices":[{"x":415,"y":960}]}],"softwareSystemId":"49"}],"systemContextViews":[{"dimensions":{"height":1442,"width":894},"elements":[{"id":"9","x":224,"y":818},{"id":"59","x":220,"y":179}],"enterpriseBoundaryVisible":true,"key":"Context_DynamicES","name":"System Context View: Dynamic ES","order":2,"relationships":[{"id":"189"}],"softwareSystemId":"9"},{"dimensions":{"height":1403,"width":893},"elements":[{"id":"23","x":221,"y":780},{"id":"59","x":221,"y":180}],"enterpriseBoundaryVisible":true,"key":"Context_SysEleven","name":"System Context View: SysEleven","order":3,"relationships":[{"id":"199"}],"softwareSystemId":"23"},{"dimensions":{"height":2149,"width":2944},"elements":[{"id":"5","x":749,"y":164},{"id":"6","x":1229,"y":159},{"id":"7","x":1719,"y":159},{"id":"9","x":644,"y":1520},{"id":"16","x":1759,"y":1525},{"id":"23","x":1209,"y":1525},{"id":"25","x":230,"y":590},{"id":"34","x":2274,"y":1090},{"id":"49","x":220,"y":1055},{"id":"53","x":2270,"y":540},{"id":"59","x":1214,"y":835}],"enterpriseBoundaryVisible":true,"key":"Context_ServiceUI","name":"System Context View: Service UI","order":7,"relationships":[{"id":"111","vertices":[{"x":920,"y":1075}]},{"id":"183","vertices":[{"x":935,"y":1265}]},{"id":"189"},{"id":"199"},{"id":"205"},{"id":"214"},{"id":"233","vertices":[{"x":1925,"y":1240}]},{"id":"242","vertices":[{"x":1985,"y":1050}]},{"id":"246"},{"id":"254"},{"id":"256"},{"id":"260"}],"softwareSystemId":"59"},{"dimensions":{"height":2343,"width":1769},"elements":[{"id":"8","x":678,"y":160},{"id":"25","x":1088,"y":895},{"id":"34","x":219,"y":910},{"id":"49","x":1098,"y":1720},{"id":"59","x":223,"y":1720}],"enterpriseBoundaryVisible":true,"key":"Context_EnergyMonitoring","name":"System Context View: Energy Monitoring","order":8,"relationships":[{"id":"214"},{"id":"223"},{"id":"227"},{"id":"265"},{"id":"267"}],"softwareSystemId":"25"},{"dimensions":{"height":2058,"width":2814},"elements":[{"id":"1","x":394,"y":164},{"id":"2","x":964,"y":164},{"id":"3","x":1589,"y":159},{"id":"4","x":2164,"y":159},{"id":"6","x":205,"y":929},{"id":"7","x":200,"y":484},{"id":"10","x":1989,"y":1424},{"id":"16","x":1194,"y":764},{"id":"59","x":364,"y":1434}],"enterpriseBoundaryVisible":true,"key":"Context_HEMS","name":"System Context View: Home Energy Management System","order":17,"relationships":[{"id":"193"},{"id":"205"},{"id":"258"},{"id":"263"},{"id":"269"},{"id":"271"},{"id":"273"},{"id":"275"}],"softwareSystemId":"16"},{"dimensions":{"height":1403,"width":1640},"elements":[{"id":"10","x":595,"y":780},{"id":"16","x":220,"y":180},{"id":"49","x":970,"y":180}],"enterpriseBoundaryVisible":true,"key":"Context_Mender","name":"System Context View: Mender IO","order":19,"relationships":[{"id":"172"},{"id":"193"}],"softwareSystemId":"10"},{"dimensions":{"height":2508,"width":2424},"elements":[{"id":"8","x":1388,"y":159},{"id":"25","x":1753,"y":1004},{"id":"34","x":964,"y":1000},{"id":"53","x":219,"y":1435},{"id":"59","x":978,"y":1884}],"enterpriseBoundaryVisible":true,"key":"Context_CentralAuth","name":"System Context View: Central Auth","order":21,"relationships":[{"id":"227"},{"id":"233","vertices":[{"x":1363,"y":1634}]},{"id":"242","vertices":[{"x":1053,"y":1649}]},{"id":"246"},{"id":"250"},{"id":"265"},{"id":"267"}],"softwareSystemId":"34"},{"dimensions":{"height":1531,"width":1640},"elements":[{"id":"34","x":220,"y":908},{"id":"53","x":580,"y":180},{"id":"59","x":970,"y":908}],"enterpriseBoundaryVisible":true,"key":"Context_FlexaGateway","name":"System Context View: Flexa Gateway Config","order":23,"relationships":[{"id":"246"},{"id":"250"}],"softwareSystemId":"53"},{"dimensions":{"height":2103,"width":1656},"elements":[{"id":"10","x":215,"y":790},{"id":"25","x":983,"y":180},{"id":"49","x":983,"y":780},{"id":"59","x":985,"y":1480}],"enterpriseBoundaryVisible":true,"key":"Context_FluxCapacitor","name":"System Context View: Flux Grid Capacitor","order":25,"relationships":[{"id":"111","vertices":[{"x":1345,"y":1295}]},{"id":"172"},{"id":"183","vertices":[{"x":1070,"y":1300}]},{"id":"223"}],"softwareSystemId":"49"}],"systemLandscapeViews":[{"dimensions":{"height":2631,"width":4461},"elements":[{"id":"1","x":205,"y":1392},{"id":"2","x":200,"y":1757},{"id":"3","x":205,"y":1022},{"id":"4","x":205,"y":652},{"id":"5","x":2860,"y":242},{"id":"6","x":2415,"y":242},{"id":"7","x":3330,"y":247},{"id":"8","x":3830,"y":1942},{"id":"9","x":1845,"y":812},{"id":"10","x":1045,"y":2007},{"id":"16","x":1040,"y":1172},{"id":"23","x":1865,"y":1482},{"id":"25","x":2755,"y":2002},{"id":"34","x":3791,"y":1286},{"id":"49","x":1880,"y":2007},{"id":"53","x":3780,"y":655},{"id":"59","x":2751,"y":1173}],"enterpriseBoundaryVisible":true,"key":"SystemLandscape_ServiceUI","name":"System Landscape View","order":1,"relationships":[{"id":"111","vertices":[{"x":2496,"y":1678}]},{"id":"172"},{"id":"183","vertices":[{"x":2711,"y":1836}]},{"id":"189"},{"id":"193"},{"id":"199"},{"id":"205"},{"id":"214"},{"id":"223"},{"id":"227"},{"id":"233"},{"id":"242","vertices":[{"x":3466,"y":1591}]},{"id":"246"},{"id":"250"},{"id":"254"},{"id":"256"},{"id":"258","vertices":[{"x":1705,"y":412}]},{"id":"260"},{"id":"263","vertices":[{"x":3530,"y":187},{"x":1285,"y":207}]},{"id":"265"},{"id":"267"},{"id":"269"},{"id":"271"},{"id":"273"},{"id":"275"}],"title":"Present Service UI Landscape"}]}}