Imported from rahim8050/NextCloud-App-Customisation (
apps/farm_intelligence_platform/AGENTS.md). Install upstream withnpx skills add rahim8050/NextCloud-App-Customisation --skill farm_intelligence_platform. Copyright stays with the author.
AGENTS.md — Nextcloud Farm Intelligence Platform app (farm_intelligence_platform)
This file is the operating contract for any automated agent (Codex/LLM) modifying this Nextcloud app.
0) Current status (read first)
- This app is in pre-integration mode for outbound DRF calls; integration-foundations work is allowed (settings UI, config keys, validators, DI wiring, minimal admin-only controllers/routes, templates + JS, and tests).
- Do not add new controllers/services/routes beyond the settings/admin foundations unless the task explicitly requests it.
- Outbound HTTP / DRF calls must remain behind the service layer (
WeatherApiClient) and must only be introduced when the task explicitly requests it. - Unknown backend details must be marked as explicit TODOs (endpoint paths, auth header name, payload schema, etc.). Do not guess.
1) Scope and boundaries
In scope
- Files under
apps/farm_intelligence_platform/only. - AppFramework code, settings UI, routes, services, tests, and docs for this app (when integration begins).
- Tooling/config needed to make the rules enforceable (composer scripts, linters, static analysis, test scaffolding).
Out of scope (do not do)
- Do not modify Nextcloud core or other apps.
- Do not introduce new external services or background jobs unless explicitly requested.
- Do not add large dependencies without justification and a summary in change notes.
1.5) Nextcloud file permissions (www-data)
- Nextcloud runs as www-data; all app PHP files must be readable by www-data.
- After editing files as your user, ensure ownership/group is *:www-data and perms are 640/664, otherwise autoload will fail with Class ... does not exist and endpoints will 500.
- Built JS/CSS assets must also be readable by www-data to avoid 500s.
Recommended defaults (if the repo is shared, keep directories group-sticky, e.g., 2770)
sudo chown -R "$(id -u)":www-data .
find . -type f -name "*.php" -exec chmod 640 {} \;
find . -type f \( -name "*.js" -o -name "*.css" \) -exec chmod 664 {} \;
find . -type d -exec chmod 2770 {} \;
- If ACLs are used, ensure www-data has read access.
Quick verification
sudo -u www-data test -r <file>(orsudo -u www-data head -n 1 <file>); if buttons return HTML error pages, checknextcloud.logfor include/class-not-found.
2) Architecture contract (must)
Layering
- Controllers: HTTP + JSON only (input validation, auth/permissions, status codes, response shaping). No business logic, no outbound HTTP.
- Service layer: All business logic and all outbound HTTP to the Django backend lives here (single client:
WeatherApiClient). - Settings: Admin-only settings UI writes config keys; the service reads config keys at runtime.
Allowed exception (integration-foundations)
- Admin settings UI may introduce:
lib/Sections/*(IIconSection)lib/Settings/*(ISettings)- A minimal admin-only controller + route to persist config (no outbound HTTP)
- Templates + JS for the settings page
- Any HTTP to DRF remains forbidden unless explicitly requested and must live only in
WeatherApiClient.
File layout (when code exists)
appinfo/(info.xml,routes.php)lib/Controller/(thin controllers)lib/Service/(WeatherApiClient, DTOs, error mapping)lib/Settings/(admin settings)
Enforceability
- Put integration logic behind one service API so tests can mock it.
- Add unit tests for the service; keep controller tests minimal and deterministic.
3) Security contract (non-negotiable)
Every “rule” here must have an enforcement path: unit tests, static analysis, and/or an explicit PR checklist item.
3.1 Outbound HTTP / SSRF defenses
Routing rule
- The only outbound target is the admin-configured
baseUrl. Never accept a user-provided full URL (scheme/host/port) in any endpoint.
Client rule
- All outbound HTTP must go through Nextcloud
OCP\Http\Client\IClientService(no raw curl, nofile_get_contents).
Redirect rule
- Redirects must be disabled. Do not follow 3xx responses (prevents redirect-to-metadata SSRF).
Timeout rule
- Enforce strict timeouts (connect + total).
timeoutSecondsmust be admin-configurable and bounded.
URL validation rule (at save-time and at request-time)
- Parse
baseUrland reject if:- scheme is not
https - URL contains embedded credentials (
user:pass@host) - host is missing
- host is
localhost
- scheme is not
- Resolve host (
A+AAAA) and reject if any resolved IP is in a blocked range (IPv4/IPv6 loopback, link-local, private, reserved, multicast, documentation ranges, CGNAT). - If DNS resolution fails, fail closed (do not attempt the request).
- Provide a documented admin-only override for local dev scenarios where HTTPS termination isn’t available:
- config key
devAllowHttp(bool, defaultfalse) - config key
allowlistHosts(string, blank by default; comma or newline separated entries) - When the override is
true,baseUrlmay behttp, but only if the host exactly matches an allowlisted entry. - Private/reserved IPs remain blocked unless the host is allowlisted while the override flag is enabled.
- config key
Enforcement
- Unit tests for URL validation, DNS/IP blocking, and redirect/timeout options.
- PR checklist item: “All outbound HTTP is via
WeatherApiClient+IClientServicewith redirects disabled + timeouts set.”
TODO (dev ergonomics): Document the allowed hosts/addresses for
devAllowHttp+allowlistHostsso audits know which entries are legitimate dev targets.
3.2 Secrets handling
Storage rule
- The
apiKeymust be stored encrypted at rest usingOCP\Security\ICryptobefore writing to app config. - Do not hash
apiKeyorhmacSecret; HMAC requires plaintext secret bytes for signing. - Store secrets encrypted at rest and decrypt only in memory right before signing or sending to the backend.
Exposure rule
- Never return secrets in any API response (including to admins).
- Exception: ONLY the admin credential generation/rotation endpoints may return the newly generated plaintext
hmacSecretonce. These responses must be admin-only, require CSRF/requesttoken + password confirmation, and includeCache-Control: no-store. - Settings UI must not display stored secrets; only allow replacing them.
Logging rule
- Never log:
- API keys/tokens
- full
Authorizationheaders - backend response bodies that could contain secrets
- Log only sanitized metadata: HTTP method, path (no query), status code, duration, request/correlation id, and a short error code.
Enforcement
- Unit test(s) for encryption-at-rest (stored value != plaintext).
- Unit test(s) for log redaction helper (no secret substrings).
- PR checklist item: “No secrets are logged or returned.”
3.3 Auth boundaries (Nextcloud-side)
Settings endpoints
- Must be admin-only.
- Must use Nextcloud PHP attributes for protection (documented here; apply when code exists):
AdminRequired/AuthorizedAdminSettingfor admin-only settings actionsPasswordConfirmationRequiredfor changing the API key / base URL
Weather proxy endpoints
- Default to authenticated Nextcloud sessions.
- Anonymous/public access must be explicitly requested, documented, and threat-modeled.
Enforcement
- Controller attributes + unit/integration tests for permissions.
- PR checklist item: “All settings writes are admin-only + password-confirmed.”
3.4 Input validation (proxy endpoints)
If/when proxy endpoints accept location parameters, validate strictly in controllers before calling services:
lat: required float, range[-90, 90]lon: required float, range[-180, 180]date: optional, strictYYYY-MM-DD(reject ambiguous formats)units: optional enum (TODO define allowed values)- Reject unknown parameters; limit string lengths; fail closed with stable error responses.
Enforcement
- Unit tests for validation (edge cases, invalid inputs).
3.5 Error normalization (no leakage)
Never expose backend stack traces, internal URLs, or raw backend payloads.
Normalized error schema (Nextcloud API responses)
{
"status": "error",
"error": {
"code": "invalid_argument|unauthorized|forbidden|backend_timeout|backend_unavailable|backend_error",
"message": "Safe human-readable message",
"requestId": "reqId-from-nextcloud-or-generated",
"details": {}
}
}
Enforcement
- Unit tests for error mapping (timeouts, non-2xx, JSON parse errors).
- PR checklist item: “Backend errors are mapped to stable codes; no raw backend errors leak.”
3.6 Dependency hygiene
- Keep dependencies pinned (lock files committed where used).
- Avoid adding new dependencies unless needed; justify in PR description.
- Run
composer audit/npm auditwhen adding or updating dependencies (expect false positives; document triage).
4) Integration handshake contract (Nextcloud ↔ Django DRF)
4.1 Config keys (must exist)
All keys are scoped to app id farm_intelligence_platform in Nextcloud app config.
baseUrl(string): required; validated per SSRF rules; no embedded credentials; HTTPS-onlyclientId(string): required; HMAC client identifierapiKey(string): optional/required TBD; stored encrypted viaICryptohmacSecret(string): stored encrypted viaICryptohmacSecretPrevious(string, optional): previous HMAC secret for rotation grace windowhmacSecretPreviousExpiresAt(int, optional): Unix timestamp for previous secret expirytimeoutSeconds(int): required; bounded (TODO define bounds; recommend 1–30)devAllowHttp(bool): defaultfalse; enables the dev allowlist override described aboveallowlistHosts(string): comma or newline separated hosts; when the dev override is enabled, only allowlisted hosts may resolve to private/reserved IPs or usehttp
Legacy snake_case keys (base_url, api_key, hmac_secret, hmac_client_id, timeout_seconds, dev_allow_insecure_local_http, dev_allowlist_hosts) are accepted on save only and are migrated one-way into the canonical schema.
4.2 Backend endpoints (placeholders; do not guess)
Document the Django API contract here and keep it updated. Until confirmed, keep explicit TODOs:
- Health check:
GET {baseUrl}/<TODO:health-path>→ 200 JSON (used byping()) - Forecast:
GET {baseUrl}/<TODO:forecast-path>?lat={lat}&lon={lon}&date={date?}→ 200 JSON - Auth header:
<TODO: header name + format>(e.g.,Authorization: Bearer …orX-Api-Key: …)
Token response envelope (contract rule)
- The integration token response may be raw JSON (
{ "access": "...", "expires_in": 300 }) or a DRF envelope ({ "status": 0, "message": "...", "data": { "access": "...", "expires_in": 300 } }). WeatherApiClientmust unwrapdatawhen present and mapstatus: 1payloads intoWeatherApiExceptionusingerrors.code/errors.reasonwhen available.
4.3 Normalized success shape (Nextcloud API responses)
Keep controller responses stable and simple:
{ "status": "ok", "data": {} }
Admin JSON Contract v1
All admin settings endpoints must return JSON shaped as:
- Success:
status: "ok"ok: truemessage: string
- Error:
status: "error"error.code: stringerror.message: stringerror.requestId: stringerror.details: object
Frontend rules:
- Treat success as
status === "ok"ORok === true(backward compatibility). - Any change to payload shape must update PHP + JS + tests in the same PR.
4.4 Observability / correlation ids
- Use Nextcloud
ILoggerwith structured context (never secrets). - Propagate a correlation id to the backend:
- Prefer incoming
X-Request-Idif present, else generate a UUID. - Send it as
X-Request-Id(or TODO if backend expects a different header).
- Prefer incoming
- Include
requestIdin error responses (and optionally in success responses if requested later).
5) Tooling and review gates (must)
All PRs for this app must satisfy:
- PHP lint, style check, static analysis, and unit tests via composer scripts documented in
apps/farm_intelligence_platform/CONTRIBUTING.md. - No real network calls in tests; mock
IClientService/IClient/IResponse. - Any new config keys/endpoints must update app docs and root docs (see
CONTRIBUTING.md).
Gate command contract
composer run gateMUST exist and MUST fail the build on:- PHP syntax errors
- coding-style violations
- static analysis violations
- failing tests
- Agents must paste gate output into the PR summary.
6) Change workflow for agents
When making changes:
-
Summarize the plan in 3–6 bullets.
-
List files you will touch.
-
Implement.
-
Add/adjust tests.
-
Update docs for any config keys or endpoints.
-
Provide a final summary (what changed + how to run gates).
-
Confirm
composer run gatepasses (or explain why and how to reproduce failures).