Browse Source

docs: bootstrap infra repos and session handover for Mailcow, Ollama, Mikrotik

Add AI-Agent-Standards templates, Zammad/Mailcow dual-domain docs, Ollama and
Mikrotik VM audits with config backup, PC-Multimedia bootstrap, geonet-console
Zammad/AI chat integration, and workspace registry updates.

Co-authored-by: Cursor <cursoragent@cursor.com>
master
Budi Setiawan 2 weeks ago
parent
commit
aa6ebf9057
  1. 182
      AI-Agent-Standards/README.md
  2. 70
      AI-Agent-Standards/cursor-rule-project-handover.mdc
  3. 56
      AI-Agent-Standards/examples/example-repo-ai-session-summary.md
  4. 58
      AI-Agent-Standards/examples/example-workspace-session.md
  5. 358
      AI-Agent-Standards/prompts/bootstrap-new-project.md
  6. 154
      AI-Agent-Standards/prompts/end-of-session-handover.md
  7. 63
      AI-Agent-Standards/prompts/generate-ai-session-summary.md
  8. 50
      AI-Agent-Standards/recommendations.md
  9. 53
      AI-Agent-Standards/templates/handover-checklist.md
  10. 41
      AI-Agent-Standards/templates/repo/.env.example
  11. 60
      AI-Agent-Standards/templates/repo/AGENTS.md
  12. 50
      AI-Agent-Standards/templates/repo/adr-template.md
  13. 78
      AI-Agent-Standards/templates/repo/ai-session-summary.md
  14. 44
      AI-Agent-Standards/templates/repo/api/README.md
  15. 46
      AI-Agent-Standards/templates/repo/changelog.md
  16. 141
      AI-Agent-Standards/templates/repo/context-index.md
  17. 30
      AI-Agent-Standards/templates/repo/guide/README.md
  18. 120
      AI-Agent-Standards/templates/repo/guide/migration.md
  19. 52
      AI-Agent-Standards/templates/repo/issues.md
  20. 71
      AI-Agent-Standards/templates/repo/project-status.md
  21. 93
      AI-Agent-Standards/templates/repo/server.md
  22. 105
      AI-Agent-Standards/templates/repo/system-requirements.md
  23. 42
      AI-Agent-Standards/templates/repo/todo.md
  24. 29
      AI-Agent-Standards/templates/repo/version.md
  25. 156
      AI-Agent-Standards/templates/workspace/Workspace-Context.md
  26. 71
      AI-Agent-Standards/templates/workspace/Workspace-Session.md
  27. 2
      ARCHITECTURE.md
  28. 33
      ArcGIS Enterprise/.env.example
  29. 32
      ArcGIS Enterprise/AGENTS.md
  30. 303
      ArcGIS Enterprise/ArcGIS Base Development/ArcGIS Server.md
  31. 109
      ArcGIS Enterprise/ArcGIS Base Development/server-audit.md
  32. 3
      ArcGIS Enterprise/docs/adr/000-template.md
  33. 14
      ArcGIS Enterprise/docs/ai-session-summary.md
  34. 6
      ArcGIS Enterprise/docs/changelog.md
  35. 20
      ArcGIS Enterprise/docs/context-index.md
  36. 3
      ArcGIS Enterprise/docs/guide/README.md
  37. 11
      ArcGIS Enterprise/docs/guide/migration.md
  38. 3
      ArcGIS Enterprise/docs/issues.md
  39. 8
      ArcGIS Enterprise/docs/project-status.md
  40. 29
      ArcGIS Enterprise/docs/server.md
  41. 8
      ArcGIS Enterprise/docs/system-requirements.md
  42. 9
      ArcGIS Enterprise/docs/todo.md
  43. 6
      ArcGIS Enterprise/docs/version.md
  44. 6
      Dockge/.env.example
  45. 33
      Dockge/AGENTS.md
  46. 3
      Dockge/docs/adr/000-template.md
  47. 14
      Dockge/docs/ai-session-summary.md
  48. 6
      Dockge/docs/changelog.md
  49. 20
      Dockge/docs/context-index.md
  50. 3
      Dockge/docs/guide/README.md
  51. 12
      Dockge/docs/guide/migration.md
  52. 3
      Dockge/docs/issues.md
  53. 8
      Dockge/docs/project-status.md
  54. 45
      Dockge/docs/server.md
  55. 8
      Dockge/docs/system-requirements.md
  56. 9
      Dockge/docs/todo.md
  57. 10
      Dockge/docs/version.md
  58. 103
      GeoNetAgent/AGENTS.md
  59. 25
      GeoNetAgent/docs/ai-session-summary.md
  60. 21
      GeoNetAgent/docs/changelog.md
  61. 5
      GeoNetAgent/docs/context-index.md
  62. 6
      GeoNetAgent/docs/guide/README.md
  63. 19
      GeoNetAgent/docs/guide/migration.md
  64. 26
      GeoNetAgent/docs/system-requirements.md
  65. 23
      GeoNetAgent/docs/version.md
  66. 2
      GeoNetAgent/infra/nginx/geonetagent-collector-public.conf
  67. 2
      Mailcow/.env.example
  68. 58
      Mailcow/AGENTS.md
  69. 25
      Mailcow/docs/adr/000-template.md
  70. 29
      Mailcow/docs/adr/ADR-0001-support-ldap-auth.md
  71. BIN
      Mailcow/docs/ai-session-summary.md
  72. 41
      Mailcow/docs/changelog.md
  73. 191
      Mailcow/docs/context-index.md
  74. 22
      Mailcow/docs/environment.md
  75. 14
      Mailcow/docs/guide/README.md
  76. 91
      Mailcow/docs/guide/migration.md
  77. 77
      Mailcow/docs/guide/outlook-imap-setup.md
  78. 28
      Mailcow/docs/issues.md
  79. 130
      Mailcow/docs/notes/gisportal-id-mail.md
  80. 128
      Mailcow/docs/notes/hybrid-migration-cpanel-mailcow.md
  81. 24
      Mailcow/docs/notes/ldap-ad-mailcow.md
  82. 68
      Mailcow/docs/project-status.md
  83. 101
      Mailcow/docs/system-requirements.md
  84. 44
      Mailcow/docs/todo.md
  85. 89
      Mailcow/docs/version.md
  86. 37
      Mailcow/scripts/fix-sogo-domain.sh
  87. 19
      Mailcow/scripts/setup-gisportal-inbound-whitelist.sh
  88. 79
      Mailcow/scripts/setup-transport-cpanel-user.sh
  89. 49
      Mailcow/scripts/switch-support-to-ldap.sh
  90. 18
      Mailcow/scripts/sync-ssl-from-nginx.sh
  91. 20
      Mikrotik/.env.example
  92. 7
      Mikrotik/.gitignore
  93. 36
      Mikrotik/AGENTS.md
  94. 12
      Mikrotik/README.md
  95. 34
      Mikrotik/backups/README.md
  96. 635
      Mikrotik/backups/mikrotik-export-2026-07-06.rsc
  97. 3
      Mikrotik/docs/adr/000-template.md
  98. 36
      Mikrotik/docs/adr/ADR-0001-hybrid-dual-wan.md
  99. 38
      Mikrotik/docs/ai-session-summary.md
  100. 128
      Mikrotik/docs/architecture/system-overview.md
  101. Some files were not shown because too many files have changed in this diff Show More

182
AI-Agent-Standards/README.md

@ -0,0 +1,182 @@ @@ -0,0 +1,182 @@
# AI Agent Documentation Standards
Standar **Project Knowledge Preservation** untuk monorepo, dioptimalkan agar agent berikutnya lanjut kerja dengan **token minimal** (< 2000 token cold start).
---
## Struktur
```
AI-Agent-Standards/
├── README.md
├── recommendations.md
├── cursor-rule-project-handover.mdc
├── prompts/
│ ├── end-of-session-handover.md
│ └── generate-ai-session-summary.md
├── templates/
│ ├── handover-checklist.md
│ ├── repo/
│ │ ├── AGENTS.md <- cold start (copy ke ROOT project)
│ │ ├── context-index.md <- peta domain (on-demand, boleh tebal)
│ │ ├── ai-session-summary.md <- rolling memory (max 80 baris)
│ │ ├── project-status.md
│ │ ├── todo.md
│ │ ├── changelog.md
│ │ ├── version.md
│ │ ├── server.md <- host/IP, docker, nginx, DB
│ │ ├── issues.md
│ │ ├── adr-template.md
│ │ ├── api/README.md
│ │ └── guide/README.md
│ └── workspace/
└── examples/
```
---
## Cold start (paling penting)
```
Workspace-Session.md (Active focus) # jika multi-project
-> {project}/AGENTS.md # ~200-400 token
-> {project}/docs/ai-session-summary.md # TL;DR + Lanjut + Jangan
-> STOP - mulai kerja
```
On-demand saja: todo, issues, api, guide, server.md, context-index, adr.
**Jangan cold-start:** changelog penuh, version history, WORKLOG, development-log.
**Rolling session memory = hanya** `docs/ai-session-summary.md`.
File kronologis lain = arsip.
---
## Read budget
| Tujuan | Baca maksimal |
|--------|----------------|
| Lanjut task yang sama | AGENTS + ai-session-summary |
| Ambil task baru | + todo.md (High) |
| Debug | + issues.md (ID terkait) |
| Deploy / koneksi / infra | + docs/server.md |
| Ubah API | + docs/api/ |
| Flow / prosedur | + docs/guide/ |
| Arsitektur | + docs/adr/ |
| Navigasi domain | + context-index (Zona B) |
---
## Batas keras tulis
| File | Batas |
|------|-------|
| `AGENTS.md` | ~40 baris |
| `ai-session-summary.md` | **80 baris / ~1500 token**; wajib TL;DR + Lanjut + Jangan |
| `Workspace-Session.md` | ~50-100 baris; wajib Active focus |
---
## Struktur per subproject
```
{project}/
├── AGENTS.md # WAJIB - cold start (root project)
├── .env.example # template URL/env (wajib)
└── docs/
├── ai-session-summary.md # rolling memory (OVERWRITE)
├── context-index.md # peta (Zona A pointers + Zona B on-demand)
├── project-status.md
├── todo.md
├── changelog.md
├── version.md
├── server.md # host/IP, runtime, DB (wajib)
├── system-requirements.md # OS, runtime, tool (wajib)
├── issues.md
├── adr/
├── api/ # jika API / framework API
└── guide/ # wajib migration.md + guide lain
```
---
## Tier handover
| Tier | Kapan | File |
|------|-------|------|
| **1 Minimal** | Perubahan kode/config | session (TL;DR) + todo + changelog + version (+ AGENTS jika perlu) |
| **Deploy** | Install ke device/server, rilis APK/binary, cutover prod | **Wajib** append `version.md` (bullet poin) + bump versi build config |
| **2 Relevan** | Kondisi terpenuhi | issues, adr, guide, server.md, system-requirements, .env.example, migration, status, context-index, api |
| **3 Monorepo** | Multi-project | Workspace-Session (Active focus) |
---
## Single owner
| Informasi | Owner |
|-----------|-------|
| Cold start / aturan kritis | `AGENTS.md` |
| Narasi sesi (rolling) | `ai-session-summary.md` |
| Priority | `todo.md` |
| Bug / RCA | `issues.md` |
| Changelog | `changelog.md` |
| Poin versi | `version.md`**wajib append setiap deploy/naik versi** |
| API | `docs/api/` |
| Flow prosedur | `docs/guide/` |
| Server / IP / runtime / DB | `docs/server.md` |
| System requirements | `docs/system-requirements.md` |
| URL/env template | `.env.example` |
| Migrasi server/domain | `docs/guide/migration.md` |
| Peta domain | `context-index.md` |
| Lintas project | `Workspace-Session.md` |
Pengetahuan penting, arsitektur, flow, inventori server, system requirements, migrasi **tidak boleh** hanya di chat. **No hardcoded URL.**
---
## Setup subproject baru
1. Copy `templates/repo/AGENTS.md` ke **root** project (sibling `docs/`)
2. Copy sisa `templates/repo/*` ke `{project}/docs/`
3. Isi AGENTS.md (pendek) dan context-index Zona A
4. Daftarkan di `Workspace-Context.md`
## Quick command
```
Lakukan Project Knowledge Preservation sesuai standar di
AI-Agent-Standards/prompts/end-of-session-handover.md
```
## Cursor
Salin `cursor-rule-project-handover.mdc` ke `.cursor/rules/`
---
## No hardcoded URL
- Semua URL/domain/host dari `.env` / env var / BuildConfig
- Template commit: `.env.example`; secret hanya di `.env` lokal (gitignore)
- Migrasi: update .env.example, server.md, guide/migration.md`r
## Bootstrap project baru (salin ke agent)
Prompt siap pakai: [prompts/bootstrap-new-project.md](prompts/bootstrap-new-project.md)
Agent akan membaca template lalu membuat AGENTS.md + docs/ untuk satu atau lebih subproject, dan mendaftarkannya di Workspace-Context.md.
Contoh:
```
Ikuti AI-Agent-Standards/prompts/bootstrap-new-project.md
projects:
- path: lagercloud.com/billing
name: Billing
description: Service tagihan
stack: Python FastAPI
type: api
has_api: true
```

70
AI-Agent-Standards/cursor-rule-project-handover.mdc

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
---
description: Wajib jalankan Project Knowledge Preservation di akhir sesi atau sebelum commit/push
alwaysApply: true
---
# End of Session - Project Handover
Sebelum menganggap pekerjaan selesai atau melakukan commit/push:
1. Jalankan **Project Knowledge Preservation** sesuai standar
2. Standar lengkap: `AI-Agent-Standards/prompts/end-of-session-handover.md`
## Tier (ringkas)
**Tier 1 - Minimal** (per project yang berubah):
- OVERWRITE `docs/ai-session-summary.md` (max 80 baris; TL;DR + Lanjut + Jangan)
- UPDATE `docs/todo.md` ("Selesai sesi ini" overwrite)
- APPEND `docs/changelog.md` + `docs/version.md` (**wajib** jika deploy/naik versi — poin-poin perubahan, jangan skip)
- UPDATE `AGENTS.md` hanya jika aturan kritis berubah
## Deploy / naik versi (wajib)
Perubahan yang di-install ke device, server, atau production:
1. Bump versi di build config (SSOT numerik)
2. Append `docs/version.md` — bullet: apa berubah, fix/feature, QC/deploy note
3. Append `docs/changelog.md` (Keep a Changelog)
4. Tier 1 session summary + todo
**Tier 2 - Jika relevan:** issues, ADR, guide, server.md, system-requirements.md, .env.example, guide/migration.md, project-status, context-index, api/
**Tier 3 - Monorepo:** OVERWRITE `Workspace-Session.md` (Active focus)
## Single Owner
- Cold start -> `AGENTS.md`
- Priority -> `todo.md`
- Bug/RCA -> `issues.md`
- Narasi sesi -> `ai-session-summary.md`
- Server/IP/DB -> `docs/server.md`
- System requirements -> `docs/system-requirements.md`
- Migrasi domain/server -> `docs/guide/migration.md` + `.env.example`
- Template URL/env -> `.env.example` (root project)
- Flow prosedur -> `docs/guide/`
- Arsitektur -> `docs/adr/`
## No hardcoded URL
- URL/domain/host:port dari env / `.env` / BuildConfig — **bukan** hardcode di source
- Migrasi: update `.env.example`, server.md, migration.md — minimal ubah kode
- Commit `.env.example` OK; `.env` dengan secret **tidak**
## Aturan
- Jangan simpan pengetahuan penting hanya di chat
- Jangan commit secret/credential
- Final Report ke user
## Urutan Baca AI Berikutnya
```
Workspace-Session (Active focus) -> AGENTS.md -> ai-session-summary (TL;DR) -> STOP
```
On-demand: todo, issues, api, guide, server.md, system-requirements, migration, context-index, adr.
## Slash Commands
- `/handover` - full tiered preservation
- `/session-summary` - hanya ai-session-summary.md
- `/workspace-summary` - hanya Workspace-Session.md

56
AI-Agent-Standards/examples/example-repo-ai-session-summary.md

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
# AI Session Summary
<!-- CONTOH - max 80 baris, omit-if-N/A -->
```yaml
session: 2026-07-03-ldap-login-fix
project: lagercloud.com/ldap
version: 1.2.0
status: ready
next: Token revocation on logout
previous: 2026-07-01-ldap-migration
agent: Cursor
branch: feat/jwt-refresh
```
---
## TL;DR
- Tambah endpoint JWT refresh token
- Perbaiki 401 loop saat token expired
- Update docs API auth
---
## Lanjut dari sini
1. Token revocation on logout
2. Integrasi refresh ke client Super App
> Backlog: [todo.md](./todo.md)
---
## Jangan
- Jangan simpan refresh token di localStorage (pakai httpOnly cookie)
- Jangan revert error code TOKEN_EXPIRED di middleware
---
## Detail
### Bug fix
| Bug | Root cause | Fix | Ref |
|-----|------------|-----|-----|
| 401 loop | Middleware throw unhandled | Return 401 + TOKEN_EXPIRED | [ISS-001](./issues.md) |
### API
| Method | Endpoint | Perubahan |
|--------|----------|-----------|
| POST | `/api/v1/auth/refresh` | Baru |
**On-demand:** [../AGENTS.md](../AGENTS.md) | [todo.md](./todo.md) | [api/](./api/)

58
AI-Agent-Standards/examples/example-workspace-session.md

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
# Workspace Session
<!-- CONTOH monorepo -->
## Active focus
- `lagercloud.com/ldap`: JWT refresh live -> next: token revocation
- `lagercloud.com/infra`: heartbeat 60s -> next: retry logic
- `lagercloud.com/Kotlin`: spec Device Request -> next: implement poll
---
## Session Information
| Field | Value |
|-------|-------|
| Tanggal | 2026-07-03 |
| Session ID | 2026-07-03-auth-agent-device |
| AI Agent | Cursor |
---
## Executive Summary
- LDAP: JWT refresh token
- infra: heartbeat timeout 60s
- Kotlin: spec Device Request
---
## Project yang dikerjakan
| Project | Path | Summary | Branch | Version |
|---------|------|---------|--------|---------|
| ldap | `lagercloud.com/ldap` | JWT refresh | feat/jwt-refresh | 1.2.0 |
| infra | `lagercloud.com/infra` | Heartbeat 60s | fix/heartbeat | 0.4.1 |
| Kotlin | `lagercloud.com/Kotlin` | Spec Device Request | docs/device-request | 1.1.2 |
---
## Cross-Project Impact
| Perubahan | Project | Dampak |
|-----------|---------|--------|
| JWT refresh | ldap | Client/agent perlu update auth |
| Heartbeat 60s | infra | Kotlin agent polling disesuaikan |
---
## Referensi detail (on-demand)
| Project | Cold start | Session | Version |
|---------|------------|---------|---------|
| ldap | [AGENTS.md](../lagercloud.com/ldap/AGENTS.md) | [session](../lagercloud.com/ldap/docs/ai-session-summary.md) | [version](../lagercloud.com/ldap/docs/version.md) |
| infra | [AGENTS.md](../lagercloud.com/infra/AGENTS.md) | [session](../lagercloud.com/infra/docs/ai-session-summary.md) | [version](../lagercloud.com/infra/docs/version.md) |
| Kotlin | [AGENTS.md](../lagercloud.com/Kotlin/AGENTS.md) | [session](../lagercloud.com/Kotlin/docs/ai-session-summary.md) | [version](../lagercloud.com/Kotlin/docs/version.md) |
**Konteks stabil:** [Workspace-Context.md](../Workspace-Context.md)

358
AI-Agent-Standards/prompts/bootstrap-new-project.md

@ -0,0 +1,358 @@ @@ -0,0 +1,358 @@
# Prompt: Bootstrap Project Docs dari AI-Agent-Standards
> **Cara pakai:** Salin seluruh isi file ini ke chat agent, lalu isi blok **INPUT** di bawah (atau sebutkan path/nama project di pesan Anda).
> **Alias:** `/bootstrap-docs`
> **Tujuan:** Agent membaca template di `AI-Agent-Standards/` lalu **membuat** struktur dokumentasi standar untuk satu atau lebih subproject baru di monorepo.
---
## INPUT (isi sebelum / saat menjalankan)
Salin dan lengkapi:
```yaml
projects:
- path: {path/relatif/dari/root monorepo} # contoh: lagercloud.com/my-service
name: {Nama Project}
description: {1-2 kalimat}
stack: {tech stack singkat}
type: app | api | library | infra | docs-only
has_api: true | false # true => buat docs/api/
has_guide: true | false # true => buat docs/guide/
version: 0.1.0
servers: # opsional; wajib diisi jika sudah diketahui
- host: "{IP atau hostname}"
env: dev|staging|prod
role: app|db|reverse-proxy|worker
apps: # docker, nginx, IIS, Apache, SQL Server, PostgreSQL, ...
- name: "{service}"
kind: Docker|nginx|IIS|Apache|SQL Server|PostgreSQL|other
port: "{port}"
path_or_container: "{compose service / site / instance}"
critical_rules: # 3-7 bullet
- "{aturan kritis 1}"
- "{aturan kritis 2}"
initial_todos: # opsional
- priority: High
item: "{task}"
```
Jika user hanya memberi daftar path tanpa YAML, tanyakan field yang kurang **sekali**, lalu lanjut. Jangan tanya berulang.
**Asumsi default jika tidak disebut:**
- `type: app`
- `has_api: false` (kecuali type = api atau nama/deskripsi menyebut API)
- `has_guide: false`
- `version: 0.1.0`
- `critical_rules`: minimal "No commit kecuali user minta", "No hardcoded URL/domain/IP — pakai .env.example", "Jangan commit .env", "Pengetahuan penting wajib ke docs bukan hanya chat"
---
## Objective
Untuk **setiap** project di INPUT:
1. Baca template dari `AI-Agent-Standards/templates/repo/`
2. Buat struktur file sesuai standar
3. Isi field wajib (bukan meninggalkan semua placeholder `{...}`)
4. Daftarkan project di `Workspace-Context.md` (root monorepo)
5. Laporkan hasil ke user
**Jangan** commit/push kecuali user meminta.
**Jangan** menimpa file yang sudah ada tanpa konfirmasi (kecuali user bilang "overwrite").
---
## Sumber template (WAJIB dibaca dulu)
Baca file berikut sebelum menulis (cukup file ini + template yang akan di-copy):
| File | Kegunaan |
|------|----------|
| `AI-Agent-Standards/README.md` | Ringkasan standar |
| `AI-Agent-Standards/templates/repo/AGENTS.md` | Cold start (ke ROOT project) |
| `AI-Agent-Standards/templates/repo/ai-session-summary.md` | Rolling session memory |
| `AI-Agent-Standards/templates/repo/context-index.md` | Peta domain |
| `AI-Agent-Standards/templates/repo/todo.md` | Priority |
| `AI-Agent-Standards/templates/repo/changelog.md` | Keep a Changelog |
| `AI-Agent-Standards/templates/repo/version.md` | Poin versi |
| `AI-Agent-Standards/templates/repo/issues.md` | Issues |
| `AI-Agent-Standards/templates/repo/project-status.md` | Status |
| `AI-Agent-Standards/templates/repo/adr-template.md` | ADR template |
| `AI-Agent-Standards/templates/repo/api/README.md` | Hanya jika has_api |
| `AI-Agent-Standards/templates/repo/guide/README.md` | Hanya jika has_guide |
| `AI-Agent-Standards/templates/repo/server.md` | Inventori server (selalu dibuat) |
| `templates/repo/system-requirements.md` | System requirements (selalu dibuat) |
| `templates/repo/.env.example` | Template URL/env (selalu dibuat) |
| `templates/repo/guide/migration.md` | Migrasi server/domain (selalu dibuat) |
| `Workspace-Context.md` | Daftarkan project + indeks server |
Encoding: tulis file sebagai **UTF-8** (tanpa UTF-16).
---
## Struktur yang harus dibuat (per project)
```
{path}/
├── AGENTS.md # cold start
├── .env.example # template URL/env (WAJIB, commit OK)
└── docs/
├── ai-session-summary.md
├── context-index.md
├── project-status.md
├── todo.md
├── changelog.md
├── version.md
├── issues.md
├── adr/
│ └── 000-template.md # dari adr-template.md
├── server.md # WAJIB - inventori host/IP/runtime/DB
├── system-requirements.md # WAJIB - OS, runtime, tool minimum
├── api/ # HANYA jika has_api: true
│ └── README.md
└── guide/ # WAJIB (minimal migration.md)
├── README.md
└── migration.md # migrasi server/domain
```
### Mapping copy
| Sumber (`AI-Agent-Standards/templates/repo/`) | Tujuan |
|-----------------------------------------------|--------|
| `AGENTS.md` | `{path}/AGENTS.md` |
| `ai-session-summary.md` | `{path}/docs/ai-session-summary.md` |
| `context-index.md` | `{path}/docs/context-index.md` |
| `project-status.md` | `{path}/docs/project-status.md` |
| `todo.md` | `{path}/docs/todo.md` |
| `changelog.md` | `{path}/docs/changelog.md` |
| `version.md` | `{path}/docs/version.md` |
| `issues.md` | `{path}/docs/issues.md` |
| `server.md` | `{path}/docs/server.md` |
| `system-requirements.md` | `{path}/docs/system-requirements.md` |
| `.env.example` | `{path}/.env.example` |
| `guide/migration.md` | `{path}/docs/guide/migration.md` |
| `adr-template.md` | `{path}/docs/adr/000-template.md` |
| `api/README.md` | `{path}/docs/api/README.md` (jika has_api) |
| `guide/README.md` | `{path}/docs/guide/README.md` |
---
## Isi wajib (ganti placeholder)
### 1. `{path}/AGENTS.md` (max ~40 baris)
- Project path monorepo
- **Apa ini?** = `description`
- **Aturan kritis** = `critical_rules` (+ default jika kurang dari 3)
- Sertakan aturan **no hardcoded URL** + link `.env.example`, `docs/system-requirements.md`, `docs/guide/migration.md`
- Pertahankan urutan baca stop-early dari template
### 2. `{path}/docs/context-index.md`
**Zona A** wajib diisi:
- Nama, path, deskripsi/stack, tipe, versi, link AGENTS + session + todo
- Last updated = tanggal hari ini
**Zona B:** biarkan tabel kosong atau minimal; hapus baris api/guide jika `has_api`/`has_guide` false.
### 3. `{path}/docs/ai-session-summary.md` (max 80 baris)
Isi sesi init:
```yaml
session: {YYYY-MM-DD}-init
project: {path}
version: {version}
status: ready
next: {item High pertama dari initial_todos, atau "Isi todo dan mulai implementasi"}
agent: {nama agent jika diketahui, else Unknown}
branch: main
```
**TL;DR:**
- Bootstrap dokumentasi standar AI-Agent-Standards
- Project `{name}` siap dilanjutkan agent berikutnya
**Lanjut dari sini:**
1. {dari initial_todos High, atau "Lengkapi spesifikasi / implementasi"}
**Jangan:**
- Jangan simpan pengetahuan penting hanya di chat
- Jangan commit secret
Hapus blok Detail jika tidak perlu.
### 4. `{path}/docs/version.md`
Entri pertama:
```markdown
## [{version}] - {YYYY-MM-DD}
- Bootstrap dokumentasi standar (AGENTS.md + docs/)
- Inisialisasi project `{name}`
```
### 5. `{path}/docs/changelog.md`
Di `[Unreleased]` atau langsung versi:
```markdown
## [{version}] - {YYYY-MM-DD}
### Added
- Struktur dokumentasi standar AI-Agent-Standards
```
### 6. `{path}/docs/todo.md`
- Masukkan `initial_todos` ke High/Medium/Low
- Seksi "Selesai (Sesi Ini)": bootstrap docs
### 7. `{path}/docs/project-status.md`
- Ringkasan = description
- Progress: Documentation = Done; implementasi sesuai status awal
- Next priority link ke todo.md
### 8. `{path}/docs/issues.md`
Biarkan struktur template (belum ada issue), ganti placeholder generik seminimal mungkin.
### 9. `{path}/docs/server.md` (WAJIB)
Salin dari `templates/repo/server.md`, lalu:
- Isi **Project** path dan **Last updated**
- Jika INPUT punya `servers:`: isi tabel Host/Server, Aplikasi & Runtime, Database, Endpoint
- Isi **Mapping ke path project** dengan path monorepo project ini
- Jika server belum diketahui: biarkan tabel dengan satu baris placeholder dan catatan `TODO: isi saat infra diketahui` — **jangan hapus file**
- Credential location saja, **jangan** password/secret
### 10. `{path}/docs/system-requirements.md` (WAJIB)
- Isi tipe project, stack dari INPUT
- Tabel runtime/tool (JDK, Docker, Android Studio, SQL, dll.) — placeholder OK dengan TODO
- Seksi **Variabel URL & konfigurasi** — list variabel dari `.env.example`
- Cantumkan aturan no hardcode
### 11. `{path}/.env.example` (WAJIB, root project)
- Salin dari `templates/repo/.env.example`
- Sesuaikan nama variabel project (`API_BASE_URL`, dll.)
- Nilai = placeholder dev, **bukan** production secret
- Tambah komentar variabel khusus project (Android: `AGENT_API_BASE_URL`, dll.)
### 12. `{path}/docs/guide/migration.md` (WAJIB)
- Salin template; isi tabel inventori **jika** server/domain sudah diketahui
- Jika belum: biarkan checklist + catatan `TODO: isi saat migrasi pertama`
- Pastikan `guide/README.md` mengindeks migration.md
---
## Update Workspace-Context.md (root monorepo)
Untuk setiap project baru, **tambah baris** di tabel Project Aktif:
| Repository | Path | Deskripsi | Status | Cold start | Context |
|------------|------|-----------|--------|------------|---------|
| `{name}` | `{path}/` | `{description}` | New | [`AGENTS.md`]({path}/AGENTS.md) | [`docs/context-index.md`]({path}/docs/context-index.md) |
Tambah baris Quick Navigation:
| Mulai kerja `{name}` | `{path}/AGENTS.md` -> `docs/ai-session-summary.md` |
Jangan hapus project yang sudah ada.
Jika ada seksi **Server Index** di `Workspace-Context.md`, tambah baris per host yang diketahui:
| Host / IP | Project | Role | Runtime ringkas | Detail |
|-----------|---------|------|-----------------|--------|
| {ip} | `{path}` | {role} | {docker/nginx/SQL/...} | [server.md]({path}/docs/server.md) |
Jika seksi belum ada, buat seksi `## Server Index (lintas project)` dengan tabel di atas (hanya indeks + link, detail di `docs/server.md`).
**Jangan** overwrite `Workspace-Session.md` kecuali user minta (boleh sebut di laporan bahwa sesi berikutnya bisa menambahkan Active focus).
---
## Aturan keamanan & kualitas
- Encoding UTF-8
- Tidak menulis secret/credential
- Tidak menghapus project/docs existing tanpa izin
- Jika `{path}` sudah punya `AGENTS.md` atau `docs/`: **stop dan tanya** overwrite / merge / skip
- Ikuti single owner: jangan menduplikasi backlog ke session summary
- Pengetahuan penting tidak boleh hanya di chat (sudah masuk docs lewat bootstrap ini)
---
## Urutan eksekusi
```
1. Parse INPUT (semua project)
2. Baca template AI-Agent-Standards
3. Untuk tiap project:
a. Cek path belum ada konflik
b. Buat folder
c. Copy + isi file wajib
4. Update Workspace-Context.md
5. Final report
```
---
## Final Report (wajib ke user)
Untuk setiap project:
- Path dibuat
- File dibuat (daftar)
- has_api / has_guide
- server.md, system-requirements.md, .env.example, guide/migration.md
- Version awal
- Link cold start: `{path}/AGENTS.md`
Workspace:
- Baris yang ditambahkan ke `Workspace-Context.md`
Langkah user berikutnya:
```
Buka {path}/AGENTS.md lalu docs/ai-session-summary.md untuk lanjut kerja.
Akhir sesi: ikuti AI-Agent-Standards/prompts/end-of-session-handover.md
```
---
## Contoh pemanggilan singkat (user)
```
Ikuti AI-Agent-Standards/prompts/bootstrap-new-project.md
projects:
- path: lagercloud.com/billing
name: Billing
description: Service tagihan Training Center
stack: Python FastAPI
type: api
has_api: true
has_guide: false
- path: lagercloud.com/notify
name: Notify
description: Notifikasi WA/email
stack: Python
type: app
has_api: true
has_guide: true
- path: tools/report-cli
name: Report CLI
description: CLI laporan internal
stack: Node.js
type: app
```
Agent wajib membuat ketiga project sesuai template tanpa menunggu instruksi tambahan selain field yang benar-benar hilang.

154
AI-Agent-Standards/prompts/end-of-session-handover.md

@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
# Prompt AI Agent - End of Session Project Knowledge Preservation
> **Trigger:** akhir sesi / sebelum commit-push. Alias: `/handover`
> **Workspace:** monorepo (satu git root, banyak subproject)
---
## Objective
Pindahkan pengetahuan sesi ke docs agar agent berikutnya lanjut dengan **token minimal** (< 2000 token cold start).
Chat **bukan** penyimpanan pengetahuan. Catatan penting, arsitektur, flow, keputusan teknis **wajib** masuk docs (owner yang benar).
---
## Scope
Identifikasi subproject yang berubah. Project tanpa perubahan: skip.
---
## Tier Handover
### Tier 1 - Minimal (WAJIB jika ada perubahan kode/config)
1. **OVERWRITE** `docs/ai-session-summary.md`
- Max 80 baris / ~1500 token
- Wajib: yaml + **TL;DR** + **Lanjut dari sini** + **Jangan**
- Detail opsional (omit-if-N/A)
- **Satu-satunya rolling session memory** - jangan andalkan WORKLOG/development-log
2. **UPDATE** `docs/todo.md`
- Seksi "Selesai (Sesi Ini)" di-overwrite (bukan menumpuk)
3. **APPEND** `docs/changelog.md` (Keep a Changelog saja)
4. **APPEND** `docs/version.md` (poin-poin versi) — **wajib** jika deploy / naik versi / install ke device atau server; format bullet seperti entri semver yang ada; jangan overwrite entri lama
5. **UPDATE** `AGENTS.md` (root project) **hanya jika** aturan kritis / pointer berubah
### Tier 2 - Jika relevan
| Kondisi | Aksi |
|---------|------|
| Bug / limitation | `docs/issues.md` |
| Keputusan arsitektur / flow sistem | `docs/adr/` |
| Flow / prosedur step-by-step | `docs/guide/` |
| Host/IP, runtime (Docker/nginx/IIS/Apache), DB, port berubah | `docs/server.md` |
| System requirement (OS, JDK, Docker, DB versi) berubah | `docs/system-requirements.md` |
| Migrasi server/domain / cutover URL | `docs/guide/migration.md` + `.env.example` |
| Variabel env/URL baru | `.env.example` (root project); kode baca env — **no hardcode** |
| Progress area | `docs/project-status.md` |
| Dokumen/struktur baru | `docs/context-index.md` |
| API berubah | `docs/api/` |
### Tier 3 - Monorepo
**OVERWRITE** `Workspace-Session.md`:
- Seksi **Active focus** di atas (1-3 baris per project)
- Tabel project + link session summary + version
- Cross-project impact
- Jangan duplikasi isi session summary
---
## Single Owner
| Informasi | Owner |
|-----------|-------|
| Cold start / aturan kritis | `AGENTS.md` (root project) |
| Narasi sesi (rolling) | `docs/ai-session-summary.md` |
| Priority / TODO | `docs/todo.md` |
| Bug / RCA | `docs/issues.md` |
| Changelog kategori | `docs/changelog.md` |
| Poin versi | `docs/version.md` |
| API | `docs/api/` |
| Step-by-step / flow prosedur | `docs/guide/` |
| Server / IP / runtime / DB | `docs/server.md` |
| System requirements | `docs/system-requirements.md` |
| Migrasi server/domain | `docs/guide/migration.md` |
| Template URL/env (dynamic) | `.env.example` (root) |
| Peta domain | `docs/context-index.md` (on-demand) |
| Lintas project sesi | `Workspace-Session.md` |
**Arsip (bukan jalur baca wajib):** WORKLOG, development-log, history panjang.
---
## Read budget (untuk AI berikutnya)
| Tujuan | Baca maksimal |
|--------|----------------|
| Lanjut task yang sama | `AGENTS.md` + `ai-session-summary` (TL;DR/Lanjut/Jangan) |
| Ambil task baru | + `todo.md` (High saja) |
| Debug | + `issues.md` (ID terkait) |
| Ubah API | + `docs/api/` |
| Ikuti prosedur | + `docs/guide/` |
| Deploy / debug koneksi / infra | + `docs/server.md` |
| Migrasi / ganti domain | + `docs/guide/migration.md` + `.env.example` |
| Setup tool chain | + `docs/system-requirements.md` |
| Keputusan arsitektur | + `docs/adr/` terkait |
| Navigasi domain | + `context-index.md` Zona B |
**Urutan cold start:**
```
Workspace-Session.md (jika multi-project; baca Active focus dulu)
-> {project}/AGENTS.md
-> {project}/docs/ai-session-summary.md
-> STOP - mulai kerja
```
Target: **< 2000 token** sebelum coding.
---
## No hardcoded URL (wajib)
- Semua **URL, domain, base path, host:port** di aplikasi harus dari **environment** atau config yang dibaca saat runtime/build (.env, env var, BuildConfig, gradle.properties).
- **Dilarang** hardcode di source code kecuali unit test mock atau .env.example placeholder.
- Saat migrasi domain/server: ubah .env / server config + update .env.example, server.md, guide/migration.md — **bukan** scatter edit URL di banyak file kode.
- Android/Kotlin: base URL lewat BuildConfig/flavor, bukan string literal di Activity/Service.
- Commit: .env.example boleh; .env dengan secret **tidak**.
---
## Knowledge compression
- Ringkas; omit-if-N/A; referensi jangan salin
- **Harus masuk docs:** solusi bug, troubleshooting, config, deploy, keputusan teknis, arsitektur, flow, **system requirements**, **inventori server**, **langkah migrasi server/domain**, root cause, workaround, dependency, versi
- **Jangan:** secret, log mentah, chat verbatim, info yang sudah di kode
---
## Validation
Lihat `templates/handover-checklist.md`.
Minimal:
- [ ] Tier 1 lengkap
- [ ] Session summary punya TL;DR + Lanjut + Jangan, <= 80 baris
- [ ] Tidak ada pengetahuan penting hanya di chat
- [ ] Tidak ada secret di diff
---
## Git
Commit Conventional Commits hanya jika user meminta. Push hanya jika diminta.
## Final Report
Project, tier, file, version, TODO/issue/ADR baru, ringkasan, next steps.

63
AI-Agent-Standards/prompts/generate-ai-session-summary.md

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
# AI Agent Command - Generate AI Session Summary
> **Trigger:** `/session-summary`
> **Target file:** `docs/ai-session-summary.md`
> **Strategi:** OVERWRITE (bukan append)
> **Batas keras:** max 80 baris / ~1500 token
> **Satu-satunya rolling session memory** per project
---
## Tujuan
Overwrite `docs/ai-session-summary.md` agar agent berikutnya lanjut kerja dari **TL;DR + Lanjut dari sini + Jangan** tanpa baca docs lain.
Target cold-start total (AGENTS + session): **< 2000 token**.
---
## Aturan tulis
1. File selalu di-overwrite
2. **Wajib di atas:** yaml metadata + TL;DR + Lanjut dari sini + Jangan
3. Blok **Detail** hanya jika perlu; hapus jika TL;DR cukup
4. Omit seksi kosong - jangan isi N/A
5. Max 80 baris total
6. "Catatan untuk AI berikutnya" max 5 bullet
7. Jangan salin changelog / daftar file / backlog todo / RCA issues
8. Jangan mengandalkan WORKLOG atau development-log sebagai pengganti file ini
---
## Struktur wajib
```yaml
session: ...
project: ...
version: ...
status: ready|blocked|in-progress
next: ...
```
## TL;DR
- max 5 bullet
## Lanjut dari sini
1. ...
2. ...
## Jangan
- max 5 bullet
## Detail (opsional)
- Perubahan material, feature, bug, config/api/db, catatan
Template: `templates/repo/ai-session-summary.md`
---
## Setelah selesai
1. Tier 1 lain: todo.md, changelog.md, version.md
2. AGENTS.md hanya jika aturan kritis berubah
3. Multi-project: Workspace-Session.md (sertakan Active focus)

50
AI-Agent-Standards/recommendations.md

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
# Rekomendasi Standar Dokumentasi AI
Status: **diadopsi ke template** (termasuk optimasi token 2026-07-04).
---
## Diadopsi
1. Session ID + yaml metadata di session summary
2. STRATEGY badge
3. Tier handover (Minimal / Relevan / Monorepo)
4. Single owner
5. Keep a Changelog saja
6. omit-if-N/A
7. todo "Selesai sesi ini" overwrite
8. context-index boleh tebal, berlapis (Zona A / Zona B)
9. Monorepo model
10. docs/api/ dan docs/guide/
11. docs/version.md — **wajib append** setiap deploy / naik versi (poin-poin perubahan)
12. Pengetahuan penting tidak boleh hanya di chat
13. **AGENTS.md cold start** (root project, max ~40 baris)
14. **Session summary:** TL;DR + Lanjut + Jangan; max 80 baris
15. **Satu rolling memory:** hanya ai-session-summary; WORKLOG/development-log = arsip
16. **Read budget** + urutan stop-early (< 2000 token)
17. **Workspace-Session Active focus**
18. **docs/server.md** - inventori host/IP, runtime, port
19. **docs/system-requirements.md** - OS, runtime, tool minimum
20. **.env.example** - template URL/env (dynamic, no hardcode)
21. **docs/guide/migration.md** - migrasi server/domain + checklist cutover
---
## Opsional (belum diwajibkan)
- `docs/.handover-meta.json` untuk CI
- Pre-commit hook otomatis: kode berubah => session summary + version harus berubah
- Wiring slash commands per agent tool
---
## Estimasi token cold start
| Skenario | File | Estimasi |
|----------|------|----------|
| Lanjut task (satu project) | AGENTS + session (TL;DR) | **~700-2000** |
| Multi-project | Workspace-Session Active focus + AGENTS + session | ~1000-2500 |
| Ambil task baru | + todo High | +200-500 |
| Debug | + issues ID | +300-800 |
Target: **< 2000 token** sebelum coding pada jalur panas.

53
AI-Agent-Standards/templates/handover-checklist.md

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
# Handover Checklist
<!-- Gunakan sebelum commit/push. Ikuti TIER. -->
## Tier 1 - Minimal
Per project yang berubah:
- [ ] `docs/ai-session-summary.md` di-**overwrite**
- [ ] yaml metadata ada
- [ ] **TL;DR** (max 5 bullet)
- [ ] **Lanjut dari sini** (1-3 item)
- [ ] **Jangan** (max 5 bullet)
- [ ] Total <= 80 baris; Detail di-omit jika tidak perlu
- [ ] `docs/todo.md` diupdate; "Selesai (Sesi Ini)" di-overwrite
- [ ] `docs/changelog.md` di-append (Keep a Changelog)
- [ ] `docs/version.md` di-append (poin-poin) — **wajib** jika deploy / naik versi / install ke device-server
- [ ] Versi build config di-bump (gradle, package.json, dll.) selaras entri `version.md`
- [ ] `AGENTS.md` diupdate **hanya jika** aturan kritis berubah
## Tier 2 - Jika relevan
- [ ] `docs/issues.md`
- [ ] `docs/adr/` (arsitektur / flow sistem)
- [ ] `docs/guide/` (prosedur step-by-step)
- [ ] `docs/project-status.md`
- [ ] `docs/context-index.md`
- [ ] `docs/api/`
- [ ] `docs/server.md` (host/IP, runtime, DB berubah)
- [ ] `docs/system-requirements.md` (OS/runtime/tool berubah)
- [ ] `.env.example` + kode **no hardcoded URL** (domain/server migrasi)
- [ ] `docs/guide/migration.md` (jika migrasi/cutover)
## Tier 3 - Monorepo
- [ ] `Workspace-Session.md` di-overwrite
- [ ] Seksi **Active focus** diisi
- [ ] Link session summary + version per project
- [ ] Cross-project impact
## Quality
- [ ] Rolling memory hanya di ai-session-summary (bukan WORKLOG sebagai pengganti)
- [ ] Tidak ada pengetahuan penting hanya di chat
- [ ] Tidak ada duplikasi owner (todo/issues/session)
- [ ] Tidak ada secret / log mentah panjang
- [ ] Agent berikutnya bisa stop setelah AGENTS + session summary
## Git
- [ ] Review perubahan
- [ ] Conventional Commit (jika diminta)
- [ ] Final report ke user

41
AI-Agent-Standards/templates/repo/.env.example

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
# .env.example — Template konfigurasi (AMAN di-commit)
# Copy ke .env di root project yang sama (JANGAN commit .env)
# Semua URL/host/domain aplikasi HARUS dibaca dari variabel ini (no hardcode di kode)
# --- Environment ---
APP_ENV=development
# development | staging | production
# --- Public URLs (ubah saat migrasi domain) ---
APP_URL=https://app-dev.example.com
API_BASE_URL=https://api-dev.example.com
# API_BASE_URL=http://10.0.0.1:8055
# --- Auth / SSO (jika ada) ---
# AUTH_ISSUER_URL=
# OIDC_REDIRECT_URI=
# --- Database (host saja di docs; credential di .env lokal) ---
DB_HOST=127.0.0.1
DB_PORT=5432
DB_NAME=myapp
DB_USER=
DB_PASSWORD=
# --- Redis / cache (opsional) ---
# REDIS_URL=redis://127.0.0.1:6379/0
# --- Internal service (monorepo) ---
# LDAP_API_URL=
# AGENT_API_URL=
# --- Android / build-time (alternatif jika tidak pakai .env runtime) ---
# AGENT_API_BASE_URL=https://agent-dev.example.com
# --- Logging / debug ---
# LOG_LEVEL=info
# DEBUG=false
# Dokumentasi variabel: docs/system-requirements.md
# Migrasi server/domain: docs/guide/migration.md
# Inventori host: docs/server.md

60
AI-Agent-Standards/templates/repo/AGENTS.md

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
# AGENTS.md - Cold Start
<!-- STRATEGY: UPDATE JARANG | Baca PERTAMA saat buka project. Max ~40 baris. -->
<!-- Lokasi: root project (sibling docs/), BUKAN di dalam docs/ -->
> Project: `{path/relatif/dari/root monorepo}`
> Sesi terakhir: [docs/ai-session-summary.md](./docs/ai-session-summary.md)
> Peta lengkap (on-demand): [docs/context-index.md](./docs/context-index.md)
> Env template: [.env.example](./.env.example)
## Apa ini?
{1-2 kalimat: apa project ini dan perannya di monorepo}
## Aturan kritis
- {aturan 1 - mis. tidak akses DB langsung}
- **No hardcoded URL/domain/IP** — baca dari `.env` / env var / BuildConfig / config
- Copy `.env.example` -> `.env` lokal; jangan commit `.env` berisi secret
- No commit kecuali user minta eksplisit
- **Deploy / rilis:** naikkan versi di build config + **wajib append** [docs/version.md](./docs/version.md) (poin-poin perubahan)
- {aturan lain - max 7 bullet total}
## Lanjut kerja (urutan baca)
```
1. AGENTS.md <- Anda di sini (~200-400 token)
2. docs/ai-session-summary.md <- TL;DR + Lanjut + Jangan
3. STOP - mulai kerja
4. On-demand:
- docs/todo.md
- docs/issues.md
- docs/api/
- docs/guide/migration.md (pindah server/domain)
- docs/system-requirements.md
- docs/server.md
- .env.example
- docs/context-index.md
```
**Jangan cold start:** changelog penuh, WORKLOG, development-log.
**Rolling session memory = hanya** `docs/ai-session-summary.md`.
## Pointer cepat
| Butuh | Buka |
|-------|------|
| Priority | [docs/todo.md](./docs/todo.md) |
| Versi / deploy | [docs/version.md](./docs/version.md) |
| Bug / RCA | [docs/issues.md](./docs/issues.md) |
| API | [docs/api/](./docs/api/) |
| Migrasi server/domain | [docs/guide/migration.md](./docs/guide/migration.md) |
| System requirements | [docs/system-requirements.md](./docs/system-requirements.md) |
| Server / infra | [docs/server.md](./docs/server.md) |
| Variabel URL/env | [.env.example](./.env.example) |
## Multi-project
`Workspace-Session.md` -> `AGENTS.md` project terkait.

50
AI-Agent-Standards/templates/repo/adr-template.md

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
# ADR-{NNN}: {Judul Keputusan}
<!-- STRATEGY: APPEND | Satu file per keputusan arsitektur -->
**Owner:** keputusan arsitektur (context, decision, alternatives, consequences).
Simpan di `docs/adr/NNN-judul-singkat.md`.
| Field | Value |
|-------|-------|
| Status | Proposed / Accepted / Deprecated / Superseded |
| Date | {YYYY-MM-DD} |
| Author | {AI Agent / Developer} |
| Session | {session-id} |
| Version | {x.y.z jika terkait rilis} |
---
## Context
{Masalah atau situasi yang memerlukan keputusan}
---
## Decision
{Keputusan yang diambil}
---
## Alternatives Considered
| Alternatif | Pro | Con |
|------------|-----|-----|
| | | |
---
## Consequences
**Positif:**
-
**Negatif / Trade-off:**
-
---
## References
- {link ke issue, PR, docs terkait}

78
AI-Agent-Standards/templates/repo/ai-session-summary.md

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
# AI Session Summary
<!-- STRATEGY: OVERWRITE | Satu-satunya rolling session memory per project -->
<!-- BATAS KERAS: max 80 baris / ~1500 token. Omit seksi N/A. -->
<!-- READ: setelah AGENTS.md. Cukup TL;DR + Lanjut + Jangan untuk lanjut kerja. -->
**Jangan duplikasi:** changelog/version (daftar file), todo (backlog), issues (RCA penuh), project-status.
File kronologis lain (WORKLOG, development-log) **bukan** pengganti file ini dan **bukan** jalur baca wajib.
---
```yaml
session: {YYYY-MM-DD-judul-singkat}
project: {path/relatif}
version: {x.y.z}
status: ready|blocked|in-progress
next: {satu baris prioritas langsung}
previous: {session-id sebelumnya atau N/A}
agent: {Cursor/Cline/dll}
branch: {branch}
```
---
## TL;DR
- {max 5 bullet - apa yang dikerjakan dan hasilnya}
-
---
## Lanjut dari sini
1. {prioritas langsung sesi berikutnya}
2. {opsional}
3. {opsional}
> Backlog penuh: [todo.md](./todo.md)
---
## Jangan
- {pitfall / regresi / hal yang tidak boleh diulang - max 5 bullet}
-
---
## Detail (hanya jika perlu)
<!-- Hapus seluruh blok Detail jika TL;DR sudah cukup. Max sisa file agar total <= 80 baris. -->
### Perubahan material
> [changelog.md](./changelog.md) | [version.md](./version.md)
- {1-5 bullet dampak - bukan path file}
### Feature
- {fitur}
### Bug fix
| Bug | Root cause | Fix | Ref |
|-----|------------|-----|-----|
| | | | [ISS-NNN](./issues.md) |
### Config / infra / API / DB
- {ringkas; API detail di [api/](./api/); server/IP/runtime di [server.md](./server.md)}
### Catatan untuk AI berikutnya
- {hanya info yang tidak ada di docs lain - max 5 bullet}
**On-demand:** [context-index.md](./context-index.md) | [todo.md](./todo.md) | [issues.md](./issues.md)

44
AI-Agent-Standards/templates/repo/api/README.md

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
# API Documentation
<!-- STRATEGY: UPDATE | Wajib jika project menerapkan API atau framework API -->
**Owner:** kontrak API (endpoint, auth, request/response, error code).
Session summary hanya ringkas perubahan — detail tetap di sini.
## Indeks Endpoint
| Method | Path | Deskripsi | Auth | Doc |
|--------|------|-----------|------|-----|
| | | | | [nama.md](./nama.md) |
## Konvensi
- Satu file per resource/domain, atau satu file OpenAPI jika lebih cocok.
- Breaking change wajib dicatat di [../changelog.md](../changelog.md) dan [../version.md](../version.md).
- Contoh request/response tanpa credential nyata.
## Template Endpoint
### `{METHOD} {/path}`
**Deskripsi:** {apa yang dilakukan}
**Auth:** {none / bearer / api-key / session}
**Request:**
```json
{}
```
**Response 200:**
```json
{}
```
**Error:**
| Code | Meaning |
|------|---------|
| | |

46
AI-Agent-Standards/templates/repo/changelog.md

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
# Changelog
<!-- STRATEGY: APPEND | Riwayat permanen — jangan overwrite -->
Format: [Keep a Changelog](https://keepachangelog.com/).
Satu format saja — jangan campur entri per-sesi terpisah.
**Owner:** riwayat perubahan permanen per kategori (Added / Changed / Fixed / Removed / Deprecated / Security).
Session summary **tidak** mengulang daftar file atau entri changelog — cukup link ke sini.
Versi rilis berpoin: lihat [version.md](./version.md).
---
## [Unreleased]
### Added
-
### Changed
-
### Fixed
-
### Removed
-
### Deprecated
-
### Security
-
---
## [x.y.z] - {YYYY-MM-DD}
### Added
- {fitur baru}
### Fixed
- {bug fix}
### Changed
- {perubahan perilaku}

141
AI-Agent-Standards/templates/repo/context-index.md

@ -0,0 +1,141 @@ @@ -0,0 +1,141 @@
# Context Index
<!-- STRATEGY: UPDATE | Peta navigasi domain - boleh tebal, berlapis -->
<!-- READ: on-demand. Cold start = ../AGENTS.md dulu, lalu ai-session-summary.md -->
> Multi-project: `Workspace-Session.md` (root) -> `{project}/AGENTS.md` -> `docs/ai-session-summary.md` -> **STOP**.
> File ini hanya jika perlu navigasi domain.
Boleh tebal (spec, architecture, parent/sibling, checklist) seperti pola Kotlin.
Tetap **indeks + link**, bukan salinan isi dokumen lain.
---
## Zona A - Always-read pointers
| Item | Value |
|------|-------|
| Nama | {REPO_NAME} |
| Path monorepo | `{path/relatif/dari/root}` |
| Cold start | [../AGENTS.md](../AGENTS.md) |
| Sesi terakhir | [ai-session-summary.md](./ai-session-summary.md) |
| Priority aktif | [todo.md](./todo.md) |
| Server / infra | [server.md](./server.md) |
| System requirements | [system-requirements.md](./system-requirements.md) |
| Env / URL template | [../.env.example](../.env.example) |
| Versi | {x.y.z} - [version.md](./version.md) |
| Stack | {TECH_STACK} |
| Tipe | {app / api / library / infra / docs-only} |
| Last updated | {YYYY-MM-DD} |
**Rolling session memory:** hanya [ai-session-summary.md](./ai-session-summary.md).
Arsip (jangan cold-start): WORKLOG, development-log, changelog penuh, version history penuh.
---
## Zona B - On-demand map
> Baca baris di bawah **hanya jika tugas menyentuh area tersebut**.
### Dokumentasi inti
| Dokumen | Isi | Kapan baca |
|---------|-----|------------|
| [ai-session-summary.md](./ai-session-summary.md) | Narasi sesi (overwrite) | Setelah AGENTS |
| [todo.md](./todo.md) | Priority aktif | Ambil / ubah task |
| [project-status.md](./project-status.md) | Progress area | Butuh status luas |
| [issues.md](./issues.md) | Bug / RCA | Debug |
| [changelog.md](./changelog.md) | Keep a Changelog | Audit riwayat |
| [version.md](./version.md) | Poin per versi | Cek versi / rilis |
| [server.md](./server.md) | Host/IP, runtime, DB, port | Deploy / debug koneksi / ubah infra |
| [system-requirements.md](./system-requirements.md) | OS, runtime, tool minimum | Setup / upgrade / migrasi |
| [guide/migration.md](./guide/migration.md) | Migrasi server/domain | Cutover URL/env |
| [../.env.example](../.env.example) | Variabel URL/host (template) | Konfigurasi dynamic URL |
| [adr/](./adr/) | Keputusan arsitektur | Ubah arsitektur |
### API dan guide (jika berlaku)
| Kondisi | Path | Kapan baca |
|---------|------|------------|
| Project API / framework API | [api/](./api/) | Ubah / konsumsi endpoint |
| Prosedur step-by-step | [guide/](./guide/) | Ikuti runbook / setup |
### Spesifikasi / architecture
| Dokumen | Path | Kapan baca |
|---------|------|------------|
| | | |
### Environment / development
| Dokumen | Path | Kapan baca |
|---------|------|------------|
| | | |
### Parent / sibling (monorepo)
| Dokumen | Path | Kapan baca |
|---------|------|------------|
| | | |
---
## Struktur kode
```
{root-project}/
├── AGENTS.md # cold start (wajib)
├── .env.example # template URL/env (commit OK)
├── docs/ # dokumentasi standar
└── {modul}/ # {deskripsi}
```
---
## Dependency
| Project / Service | Relasi |
|-------------------|--------|
| | |
---
## Environment
> Detail inventori server: **[server.md](./server.md)** (single owner).
| Env | URL / Host | Catatan |
|-----|------------|---------|
| dev | | ringkas; detail di server.md |
Credential: {lokasi vault/env} - jangan simpan secret di docs.
---
## Aturan kritis AI
> Duplikat singkat dari AGENTS.md - detail aturan di AGENTS.md.
- Minimal diff - fokus scope project ini.
- No commit kecuali user minta.
- Single owner: priority -> todo; bug -> issues; sesi -> ai-session-summary.
- Perubahan material -> version.md + changelog.md.
- Host/IP/runtime/DB berubah -> update server.md.
- Tool/OS requirement berubah -> update system-requirements.md.
- Migrasi server/domain -> update guide/migration.md, .env.example, server.md; **no hardcoded URL** di kode.
- Pengetahuan penting tidak boleh hanya di chat.
- Implementasi tanpa Tier 1 handover = belum selesai.
---
## Checklist
### Sebelum implementasi
- [ ] Baca AGENTS.md + ai-session-summary (TL;DR / Lanjut / Jangan)
- [ ] todo / issues hanya jika perlu
### Setelah implementasi (Tier 1)
- [ ] Overwrite ai-session-summary (max 80 baris; TL;DR wajib)
- [ ] Update todo.md
- [ ] Append changelog.md + version.md
- [ ] Update AGENTS.md hanya jika aturan kritis berubah

30
AI-Agent-Standards/templates/repo/guide/README.md

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
# Guides
<!-- STRATEGY: UPDATE | Wajib jika project punya prosedur step-by-step khusus -->
**Owner:** panduan operasional berurutan (setup, deploy, runbook, migrasi, troubleshooting alur).
## Indeks Guide
| Guide | Path | Kapan dipakai |
|-------|------|---------------|
| Migrasi server/domain | [migration.md](./migration.md) | Pindah server, ganti IP/domain, cutover env |
| | [nama.md](./nama.md) | |
**Wajib ada:** [migration.md](./migration.md) untuk semua project (isi saat migrasi atau placeholder checklist).
## Template Guide tambahan
# {Judul Guide}
## Prasyarat
- {prasyarat}
## Langkah
1. {langkah 1}
## Verifikasi
- {cara memastikan berhasil}
## Rollback
- {jika gagal}

120
AI-Agent-Standards/templates/repo/guide/migration.md

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
# Migration Guide — Server & Domain
<!-- STRATEGY: UPDATE | Owner prosedur migrasi infra/domain project ini -->
<!-- READ: saat pindah server, ganti IP, ganti domain, atau cutover env -->
**Owner:** checklist migrasi server/domain, urutan cutover, verifikasi, rollback.
Inventori host: [../server.md](../server.md). Requirement tool: [../system-requirements.md](../system-requirements.md).
Template env: `../../.env.example` (root project).
**Prinsip:** URL/domain **dynamic** — ubah `.env` / config, bukan source code (kecuali refactor env var baru).
---
## Kapan pakai guide ini
- Migrasi app/service ke server baru (IP/host berubah)
- Ganti domain (mis. `old.example.com` -> `new.example.com`)
- Pindah database ke host/instance baru
- Cutover staging -> prod dengan URL baru
---
## Inventori sebelum migrasi
| Item | Nilai saat ini | Nilai target | File yang harus diupdate |
|------|----------------|--------------|--------------------------|
| Domain / base URL | | | `.env.example`, `.env` (lokal), [server.md](../server.md) |
| API base URL | | | kode baca env saja |
| DB host | | | `.env.example`, server.md |
| Reverse proxy | | | nginx/IIS config path di repo |
| SSL / cert | | | |
| DNS record | | | catatan di server.md |
---
## Variabel env yang terpengaruh
Salin dari `.env.example`, isi kolom old/new:
| Variabel | Old | New | Sudah di `.env` prod? |
|----------|-----|-----|------------------------|
| `API_BASE_URL` | | | |
| `APP_URL` | | | |
| `DB_HOST` | | | |
| `{CUSTOM}` | | | |
Commit yang boleh: `.env.example`, docs — **jangan** commit `.env` berisi secret.
---
## Langkah migrasi (server)
1. **Persiapan**
- [ ] Backup data & config server lama
- [ ] Server baru memenuhi [system-requirements.md](../system-requirements.md)
- [ ] Install runtime/stack (Docker, nginx, DB, dll.) di server baru
- [ ] Siapkan `.env` di server baru dari `.env.example`
2. **Deploy ke server baru (parallel)**
- [ ] Deploy kode/config ke server baru
- [ ] Point `.env` ke dependency internal (DB, Redis, dll.)
- [ ] Smoke test internal (IP/host langsung, belum DNS publik)
3. **Cutover**
- [ ] Update DNS / firewall / load balancer
- [ ] Update reverse proxy (nginx/IIS) ke backend baru
- [ ] Update `.env` production dengan URL/host final
- [ ] Restart service / container
4. **Verifikasi**
- [ ] Health check endpoint
- [ ] Login / auth flow
- [ ] Integrasi ke project monorepo lain (jika ada)
- [ ] Client app (APK/web) masih pakai env — rebuild hanya jika BuildConfig berubah
5. **Dokumentasi (wajib)**
- [ ] Update [../server.md](../server.md) (host, URL, mapping path)
- [ ] Update `.env.example` jika nama variabel/placeholder berubah
- [ ] Append entri migrasi di [../version.md](../version.md) + [../changelog.md](../changelog.md)
- [ ] Overwrite [../ai-session-summary.md](../ai-session-summary.md) (Tier 1 handover)
---
## Langkah migrasi (domain saja, server sama)
1. [ ] Tambah DNS / cert untuk domain baru
2. [ ] Update reverse proxy `server_name` / binding
3. [ ] Ubah `.env`: `API_BASE_URL`, `APP_URL`, `CORS_ORIGIN`, dll.
4. [ ] Redirect domain lama -> baru (opsional, periode transisi)
5. [ ] Update docs: server.md, .env.example
6. [ ] Smoke test dari client dengan URL baru
**Tidak perlu** ubah kode jika semua URL sudah dari env.
---
## Android / mobile (Kotlin Agent dll.)
| Area | Saat migrasi domain |
|------|---------------------|
| Base URL API | `gradle.properties` / `BuildConfig` / flavor — bukan hardcode di `.kt` |
| Network security | `network_security_config.xml` jika domain/cert berubah |
| Release | Rebuild APK jika BuildConfig berubah; OTA channel sesuai [release guide] |
---
## Rollback
1. Kembalikan DNS / proxy ke server/domain lama
2. Restore `.env` backup
3. Restart service
4. Catat rollback di [../issues.md](../issues.md) + session summary
---
## Riwayat migrasi
| Tanggal | Dari | Ke | Session / versi | Catatan |
|---------|------|-----|-----------------|---------|
| {YYYY-MM-DD} | {old host/domain} | {new} | {session-id} | |

52
AI-Agent-Standards/templates/repo/issues.md

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
# Issues
<!-- STRATEGY: APPEND | Tandai resolved, jangan hapus history -->
**Owner:** detail bug, root cause, workaround, limitation (single source).
Session summary dan project-status hanya merujuk ID issue — jangan salin RCA penuh.
---
## Open Issues
### ISS-{NNN}: {Judul}
| Field | Value |
|-------|-------|
| Status | Open |
| Severity | Critical/High/Medium/Low |
| Found | {YYYY-MM-DD} |
| Session | {session-id} |
**Deskripsi:**
{deskripsi singkat}
**Root Cause:**
{root cause jika diketahui}
**Workaround:**
{workaround jika ada}
**Files terkait:**
- `path/to/file`
---
## Resolved Issues
### ISS-{NNN}: {Judul}
| Field | Value |
|-------|-------|
| Status | Resolved |
| Resolved | {YYYY-MM-DD} |
| Fix | {ringkasan fix} |
| Version | {x.y.z — lihat version.md} |
---
## Known Limitations
| ID | Limitation | Reason | Ref |
|----|------------|--------|-----|
| LIM-001 | | | |

71
AI-Agent-Standards/templates/repo/project-status.md

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
# Project Status
<!-- STRATEGY: UPDATE | Status berkelanjutan proyek -->
> Last updated: {YYYY-MM-DD}
> Session: [ai-session-summary.md](./ai-session-summary.md)
**Owner:** ringkasan progress area (Done / In Progress / Blocked / Planned), blocker, technical debt ringkas.
**Bukan owner:** daftar TODO (lihat todo.md), detail bug (lihat issues.md), narasi sesi (lihat session summary).
---
## Ringkasan
{1-2 kalimat status proyek saat ini}
---
## Progress
| Area | Status | Catatan |
|------|--------|---------|
| {area} | {Done/In Progress/Blocked/Planned} | |
---
## Fitur Selesai
- [x] {fitur}
---
## Fitur Sedang Dikerjakan
- [ ] {fitur} - {progress%} - {catatan}
---
## Technical Debt (ringkas)
> Item actionable: [todo.md](./todo.md#technical-debt)
| Item | Prioritas |
|------|-----------|
| | High/Medium/Low |
---
## Known Issues (indeks)
> Detail: [issues.md](./issues.md)
| ID | Issue | Status |
|----|-------|--------|
| | | Open/Resolved |
---
## Blocker
| Blocker | Impact | Action |
|---------|--------|--------|
| | | |
---
## Next Priority
> Sumber kebenaran: [todo.md](./todo.md) (High Priority)
1. {mirror 1-3 item teratas dari todo - atau cukup link}

93
AI-Agent-Standards/templates/repo/server.md

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
# Server Inventory
<!-- STRATEGY: UPDATE | Owner inventori server/infra project ini -->
<!-- READ: on-demand saat deploy, debug koneksi, atau ubah infra -->
<!-- JANGAN simpan password, private key, connection string ber-secret -->
**Owner:** host/IP, role, aplikasi runtime, database, port, env yang dipakai **project ini**.
Indeks lintas project: monorepo root `Workspace-Context.md` (dan `SSH-HOSTS.md` jika ada) — link ke file ini, jangan duplikasi detail penuh.
Cold start: cukup tahu path file ini dari [AGENTS.md](../AGENTS.md) / [context-index.md](./context-index.md).
---
## Ringkasan
| Field | Value |
|-------|-------|
| Project | `{path/relatif monorepo}` |
| Last updated | {YYYY-MM-DD} |
| Credential location | {vault / .env lokal — lihat .env.example} — **bukan** secret di sini |
---
## Host / Server
| Host / IP | Env | Role | OS | Akses | Catatan |
|-----------|-----|------|-----|-------|---------|
| {10.x.x.x atau hostname} | dev/staging/prod | {app / db / reverse-proxy / worker} | {Linux/Windows} | {SSH user@host / RDP} | |
> Satu baris per host yang **benar-benar dipakai** project ini.
---
## Aplikasi & Runtime per Host
| Host / IP | Aplikasi | Jenis | Port | Path / Container | Catatan |
|-----------|----------|-------|------|------------------|---------|
| {ip} | {nama service} | Docker / nginx / IIS / Apache / systemd / Windows Service / other | {port} | {compose service / site name / unit} | |
| {ip} | {nama} | SQL Server / PostgreSQL / MySQL / Redis / other | {port} | {instance / db name} | |
Jenis yang sering dipakai (isi yang relevan saja):
- **Web / proxy:** nginx, IIS, Apache, Caddy, Traefik
- **Runtime:** Docker, Docker Compose, Kubernetes, systemd, Windows Service
- **App:** FastAPI, Node, .NET, PHP, Java, dll.
- **Data:** SQL Server, PostgreSQL, MySQL, MariaDB, Redis, MongoDB
- **Lain:** MQTT, RabbitMQ, MinIO, dll.
---
## Database
| Host / IP | Engine | Instance / Port | Database | Dipakai untuk | Credential location |
|-----------|--------|-----------------|----------|---------------|---------------------|
| | SQL Server / PostgreSQL / ... | | | | {env var / vault} |
---
## Endpoint & URL
> Nilai runtime: variabel di .env.example (root project). **Jangan hardcode** di kode.
> Migrasi domain: [guide/migration.md](./guide/migration.md)
| Env | URL / Host:Port | Env var | Catatan |
|-----|-----------------|--------------|---------|
| dev | | API_BASE_URL / APP_URL | |
| staging | | | |
| prod | | | |
---
## Mapping ke path project (monorepo)
| Komponen di server | Path di monorepo | Keterangan |
|--------------------|------------------|------------|
| {service / container / site} | `{path/relatif}/` | Kode / config utama |
| {nginx config / compose} | `{path}/...` | Jika config ikut di repo |
---
## Dependency server ke project lain
| Host / Service | Project monorepo | Relasi |
|----------------|------------------|--------|
| | `{path/}` | API / DB / shared network |
---
## Catatan operasional
- {firewall, VPN, ZeroTier, port expose, backup, dll. — tanpa secret}
**Jangan:** password, API key, private key, connection string lengkap ber-credential.
**Ganti dengan:** lokasi credential + nama variabel env.

105
AI-Agent-Standards/templates/repo/system-requirements.md

@ -0,0 +1,105 @@ @@ -0,0 +1,105 @@
# System Requirements
<!-- STRATEGY: UPDATE | Owner spesifikasi minimum runtime/build/deploy project ini -->
<!-- READ: on-demand saat setup baru, upgrade tool, atau migrasi server -->
**Owner:** versi minimum OS, runtime, tool chain, dependency infra yang **wajib** agar project jalan.
Bukan duplikasi `server.md` (host/IP) — fokus **apa yang harus terpasang**, bukan di server mana.
Credential & URL: lihat `.env.example` (root project) + [server.md](./server.md).
---
## Ringkasan
| Field | Value |
|-------|-------|
| Project | `{path/relatif monorepo}` |
| Tipe | app / api / library / infra / android |
| Last updated | {YYYY-MM-DD} |
---
## Platform & OS
| Komponen | Minimum | Direkomendasikan | Catatan |
|----------|---------|------------------|---------|
| OS dev | {Windows 11 / Ubuntu 22.04 / macOS 14} | | |
| OS server | {Linux / Windows Server} | | lihat server.md |
| Arsitektur | {x64 / arm64} | | |
---
## Runtime & Toolchain
| Tool | Versi minimum | Versi dipakai saat ini | Install / cek |
|------|---------------|------------------------|---------------|
| {Node.js / Python / JDK / .NET / Go} | | | `{command --version}` |
| {Package manager} | | | |
| {Docker / Docker Compose} | | | |
| {Android Studio / Gradle / AGP} | | | khusus Android |
| {SQL client / ODBC} | | | |
---
## Dependency eksternal (wajib tersedia)
| Dependency | Versi | Dipakai untuk | Owner docs |
|------------|-------|---------------|------------|
| {PostgreSQL / SQL Server / Redis} | | | [server.md](./server.md) |
| {nginx / IIS / Apache} | | reverse proxy | [server.md](./server.md) |
| {LDAP / Authelia / MQTT} | | | |
---
## Resource minimum (server / container)
| Lingkungan | CPU | RAM | Disk | Catatan |
|------------|-----|-----|------|---------|
| dev | | | | |
| staging | | | | |
| prod | | | | |
---
## Variabel URL & konfigurasi (no hardcode)
> Semua base URL / host / domain **harus** dari env atau config — lihat `.env.example` di root project.
| Variabel | Wajib | Contoh (bukan production) | Dipakai oleh |
|----------|-------|---------------------------|--------------|
| `API_BASE_URL` | ya | `https://api-dev.example.com` | app client |
| `{VAR_LAIN}` | | | |
**Aturan implementasi:**
- Jangan hardcode URL/domain/IP di source code, config commit, atau docs operasional (kecuali `.env.example` placeholder).
- Android: `BuildConfig`, `gradle.properties`, network security config — bukan string literal di `.kt`.
- Backend: baca dari environment / `.env` (lokal, gitignore).
---
## Port & protokol
| Service | Port | Protokol | Catatan |
|---------|------|----------|---------|
| | | HTTP/HTTPS/TCP | |
---
## Checklist setup baru (developer / server)
- [ ] Penuhi runtime minimum di tabel di atas
- [ ] Copy `.env.example` -> `.env` (lokal, jangan commit `.env`)
- [ ] Isi URL sesuai env (dev/staging/prod)
- [ ] Verifikasi koneksi ke dependency di [server.md](./server.md)
- [ ] Smoke test sesuai [guide/migration.md](./guide/migration.md) jika migrasi
---
## Perubahan requirement
Setiap upgrade tool major, OS baru, atau dependency baru:
1. Update file ini
2. Update [version.md](./version.md) + [changelog.md](./changelog.md)
3. Update `.env.example` jika variabel URL berubah
4. Migrasi server/domain: ikuti [guide/migration.md](./guide/migration.md)

42
AI-Agent-Standards/templates/repo/todo.md

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
# TODO
<!-- STRATEGY: ACTIVE UPDATE | Priority aktif = single owner di file ini -->
> Last updated: {YYYY-MM-DD}
> Sesi terakhir: [ai-session-summary.md](./ai-session-summary.md)
**Owner:** daftar pekerjaan aktif (High / Medium / Low) dan technical debt actionable.
Dokumen lain hanya merujuk ke sini — jangan menduplikasi daftar priority.
---
## High Priority
- [ ] {task} - {catatan singkat}
---
## Medium Priority
- [ ] {task}
---
## Low Priority
- [ ] {task}
---
## Technical Debt
- [ ] {debt item} - {prioritas}
---
## Selesai (Sesi Ini)
<!-- OVERWRITE tiap sesi — jangan menumpuk history lintas sesi.
Riwayat permanen: changelog.md + version.md -->
- [x] {task} - selesai {YYYY-MM-DD}

29
AI-Agent-Standards/templates/repo/version.md

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
# Version History
<!-- STRATEGY: APPEND | Satu entri per perbaikan/perubahan — jangan overwrite entri lama -->
**Owner:** riwayat versi repo dalam bentuk poin-poin per rilis/perubahan.
Setiap perbaikan atau perubahan kode/config yang material **wajib** menambah entri di sini.
**Wajib update sebelum deploy** (install ke device, push server, rilis APK/binary, cutover prod):
1. Naikkan versi di source of truth build (`build.gradle.kts`, `package.json`, `pyproject.toml`, dll.)
2. **Append** entri baru di file ini — bullet poin jelas (apa diubah, mengapa, QC jika ada)
3. Jangan overwrite entri lama (strategi **APPEND**)
Gunakan semver jika memungkinkan (`MAJOR.MINOR.PATCH`), atau tanggal+urutan (`YYYY.MM.DD.N`) jika belum ada skema versi.
Sinkron dengan [changelog.md](./changelog.md): changelog = kategori Keep a Changelog; file ini = ringkasan berpoin per versi.
---
## [x.y.z] - {YYYY-MM-DD}
- {perubahan 1}
- {perubahan 2}
- {perubahan 3}
---
## [0.1.0] - {YYYY-MM-DD}
- Inisialisasi dokumentasi standar repo

156
AI-Agent-Standards/templates/workspace/Workspace-Context.md

@ -0,0 +1,156 @@ @@ -0,0 +1,156 @@
# Workspace Context
<!-- STRATEGY: UPDATE JARANG | Konteks global monorepo -->
> Workspace ini adalah **monorepo** (satu git root, banyak subproject).
> "Repository" = **subproject** (folder project), bukan git repo terpisah.
> Sesi hari ini: [Workspace-Session.md](./Workspace-Session.md)
> Standar: `AI-Agent-Standards/`
---
## Overview
| Field | Value |
|-------|-------|
| Workspace | {NAMA_WORKSPACE} |
| Tipe | Monorepo |
| Root Git | `{path-root}` |
| Remote | `{url}` |
| Owner | {nama/team} |
| Last updated | {YYYY-MM-DD} |
---
## Urutan baca AI (hemat token)
```
1. Workspace-Session.md <- Active focus dulu
2. {project}/AGENTS.md <- cold start (~200-400 token)
3. {project}/docs/ai-session-summary.md <- TL;DR + Lanjut + Jangan
4. STOP - mulai kerja
5. On-demand: todo, issues, api, guide, server.md, context-index, adr
```
**Target cold start: < 2000 token.**
**Jangan baca di cold start:** changelog penuh, version history, WORKLOG, development-log, seluruh context-index.
**Rolling session memory per project = hanya** `docs/ai-session-summary.md`.
---
## Read budget
| Tujuan | Baca maksimal |
|--------|----------------|
| Lanjut task yang sama | AGENTS + ai-session-summary |
| Ambil task baru | + todo.md (High) |
| Debug | + issues.md (ID terkait) |
| Ubah API | + docs/api/ |
| Ikuti prosedur / flow | + docs/guide/ |
| Deploy / debug koneksi / infra | + docs/server.md |
| Arsitektur | + docs/adr/ |
| Navigasi domain | + context-index.md (Zona B) |
---
## Project Index
| Project | Path | Deskripsi | Cold start | Context |
|---------|------|-----------|------------|---------|
| {name} | `{path/}` | | [`AGENTS.md`]({path}/AGENTS.md) | [`docs/context-index.md`]({path}/docs/context-index.md) |
### Shared resources (bukan project)
| Folder | Fungsi |
|--------|--------|
| | |
---
## Dependency antar project
| From | To | Tipe | Catatan |
|------|-----|------|---------|
| | | | |
---
## Single owner (anti-duplikasi)
| Informasi | Owner |
|-----------|-------|
| Cold start / aturan kritis | `{project}/AGENTS.md` |
| Narasi sesi (rolling) | `{project}/docs/ai-session-summary.md` |
| Priority / TODO | `{project}/docs/todo.md` |
| Bug / RCA | `{project}/docs/issues.md` |
| Changelog | `{project}/docs/changelog.md` |
| Poin versi | `{project}/docs/version.md` |
| API | `{project}/docs/api/` |
| Flow / step-by-step | `{project}/docs/guide/` |
| Server / IP / runtime / DB | `{project}/docs/server.md` |
| System requirements | `{project}/docs/system-requirements.md` |
| URL/env template | `{project}/.env.example` |
| Migrasi server/domain | `{project}/docs/guide/migration.md` |
| Peta domain (on-demand) | `{project}/docs/context-index.md` |
| Lintas project sesi | `Workspace-Session.md` |
| Peta monorepo stabil | `Workspace-Context.md` |
Arsip (bukan jalur wajib): WORKLOG, development-log.
---
## Tier handover
### Tier 1 - Minimal
1. OVERWRITE `docs/ai-session-summary.md` (max 80 baris; TL;DR + Lanjut + Jangan)
2. UPDATE `docs/todo.md`
3. APPEND `docs/changelog.md` + `docs/version.md`
4. UPDATE `AGENTS.md` hanya jika aturan kritis berubah
### Tier 2 - Jika relevan
issues, adr, guide, server.md, system-requirements.md, .env.example, guide/migration.md, project-status, context-index, api/
### Tier 3 - Monorepo
OVERWRITE `Workspace-Session.md` (wajib **Active focus**)
---
## Environment global
| Env | Catatan |
|-----|---------|
| dev | |
Credential: {vault/path} - jangan simpan secret.
---
## Server Index (lintas project)
> Indeks saja. Detail per project: `{project}/docs/server.md`.
> Inventori host bersama juga boleh di `SSH-HOSTS.md` — tetap link ke server.md project terkait.
| Host / IP | Project monorepo | Role | Runtime ringkas | Detail |
|-----------|------------------|------|-----------------|--------|
| {10.x.x.x} | `{path}` | app/db/proxy | Docker, nginx, SQL Server, ... | [server.md]({path}/docs/server.md) |
Satu host boleh muncul di beberapa project jika di-share; detail role per project tetap di `docs/server.md` masing-masing.
---
## Standar dan perintah
Standar: `AI-Agent-Standards/`
| Perintah | Aksi |
|----------|------|
| `/handover` | Full tiered preservation |
| `/session-summary` | Overwrite ai-session-summary.md |
| `/workspace-summary` | Overwrite Workspace-Session.md |
Pengetahuan penting, arsitektur, flow, **dan inventori server (IP + runtime)** yang disepakati **tidak boleh** hanya di chat AI.

71
AI-Agent-Standards/templates/workspace/Workspace-Session.md

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
# Workspace Session
<!-- STRATEGY: OVERWRITE | Ringkasan lintas-project sesi ini (monorepo) -->
<!-- READ: baca Active focus dulu; detail project on-demand via link -->
<!-- BATAS: max ~50 baris / ~100 baris jika banyak project -->
---
## Active focus
<!-- 1-3 baris per project yang dikerjakan - agent scan ini dulu -->
- `{path}`: {status singkat} -> next: {prioritas langsung}
- `{path}`: {status singkat} -> next: {prioritas langsung}
---
## Session Information
| Field | Value |
|-------|-------|
| Tanggal | {YYYY-MM-DD} |
| Session ID | {YYYY-MM-DD-judul} |
| AI Agent | {Cursor/Cline/dll} |
| Developer | {nama jika ada} |
---
## Executive Summary
- {max 5 bullet lintas project}
---
## Project yang dikerjakan
| Project | Path | Summary | Branch | Version |
|---------|------|---------|--------|---------|
| | `{path/}` | | | {x.y.z} |
---
## Cross-Project Impact
| Perubahan | Project | Dampak ke Project Lain |
|-----------|---------|------------------------|
| | | |
---
## Referensi detail (on-demand)
| Project | Cold start | Session | Version |
|---------|------------|---------|---------|
| | [`AGENTS.md`]({path}/AGENTS.md) | [`ai-session-summary.md`]({path}/docs/ai-session-summary.md) | [`version.md`]({path}/docs/version.md) |
---
## Next Priority (workspace)
> Detail: masing-masing `docs/todo.md`
1. {priority lintas-project}
---
## Catatan lintas-project
- {max 5 bullet}
**Konteks stabil:** [Workspace-Context.md](./Workspace-Context.md)

2
ARCHITECTURE.md

@ -169,7 +169,7 @@ sequenceDiagram @@ -169,7 +169,7 @@ sequenceDiagram
FE->>Console: POST /api/v1/feedback\n{type, title, message, context}
Console->>Console: Simpan ke tabel feedback
Console-->>FE: {id, status: open}
Console-)Zammad: POST /api/v1/tickets ⏳\nauto-create tiket
Console-)Zammad: POST /api/v1/tickets ✅\nauto-create tiket (geonet-console `ZammadTicketService`)
FE-->>User: Tampilkan nomor tiket + pesan sukses
```

33
ArcGIS Enterprise/.env.example

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
# ArcGIS Enterprise — .env.example (template, aman di-commit)
# Salin ke .env lalu isi username & password
# =============================================================================
# SQL Server — main-db 10.100.1.25
# =============================================================================
MSSQL_HOST=10.100.1.25
MSSQL_PORT=1433
MSSQL_DATABASE=master
MSSQL_USERNAME=sa
MSSQL_PASSWORD=change-me
MSSQL_ENCRYPT=false
MSSQL_TRUST_SERVER_CERTIFICATE=true
# =============================================================================
# PostgreSQL — main-db 10.100.1.25
# =============================================================================
POSTGRES_HOST=10.100.1.25
POSTGRES_PORT=5432
POSTGRES_DATABASE=geonetagent_dev
POSTGRES_USERNAME=geonet_collector
POSTGRES_PASSWORD=change-me
# DATABASE_URL=postgresql://geonet_collector:change-me@10.100.1.25:5432/geonetagent_dev
# =============================================================================
# ArcGIS topology
# =============================================================================
ARCGIS_BASE_HOST=10.100.1.51
ARCGIS_PORTAL_URL=https://dev.gisportal.id/portal
ARCGIS_SERVER_HOST=10.100.1.51
ARCGIS_DS_OBJECT_HOST=10.100.1.52
ARCGIS_DS_SPATIO_HOST=10.100.1.53

32
ArcGIS Enterprise/AGENTS.md

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
# AGENTS.md — Cold Start
> Project: ArcGIS Enterprise/
> Sesi: [docs/ai-session-summary.md](./docs/ai-session-summary.md)
> Peta: [docs/context-index.md](./docs/context-index.md)
## Apa ini?
ESRI ArcGIS Enterprise dev — Portal, Server, Web Adaptor, Data Stores
## Aturan kritis
- dev.gisportal.id DNS langsung ke .51 — bypass reverse proxy
- No commit license / credential ESRI
- Perubahan topology → append docs/version.md + ADR
- **No hardcoded URL** — .env.example
- No commit kecuali user minta
- Pengetahuan penting wajib ke docs
## Lanjut kerja
1. AGENTS.md → docs/ai-session-summary.md → STOP
2. docs/server.md, docs/todo.md
## Pointer
| Butuh | Buka |
|-------|------|
| Versi | [docs/version.md](./docs/version.md) |
| Server | [docs/server.md](./docs/server.md) |
| Migrasi | [docs/guide/migration.md](./docs/guide/migration.md) |

303
ArcGIS Enterprise/ArcGIS Base Development/ArcGIS Server.md

@ -0,0 +1,303 @@ @@ -0,0 +1,303 @@
# ArcGIS Server — DEV11 (ArcGIS Base Development)
**Terakhir diaudit:** 6 Jul 2026 12:36 WIB
**Host:** `10.100.1.51` (DEV11) · **Versi:** ArcGIS Server 12.0.58905
**URL publik:** `https://dev.gisportal.id/server`
## Akses
```text
ssh dev51-base
ArcGIS Server Manager : https://dev.gisportal.id/server/admin
REST Services Directory: https://dev.gisportal.id/server/rest/services
```
---
## Status Saat Audit (6 Jul 2026)
| Item | Status |
|------|--------|
| Windows Service `ArcGIS Server` | ✅ Running (Auto) |
| ArcGIS Server internal port `6099` | ✅ Listening |
| Web Adaptor HTTPS internal `6443` | ✅ Listening (javaw) |
| IIS App Pool `ArcGISWebAdaptorAppPoolserver` | ✅ Started |
| REST publik (Herewego sample) | ✅ HTTP 200 |
| Uptime server | ⚠ **Baru reboot** 06 Jul 2026 11:09 WIB (~1,5 jam sebelum audit) |
> Server baru saja di-restart pagi ini. Pola ini **sesuai laporan issue**: service normal setelah reboot Windows, lalu error setelah beberapa jam.
---
## Service Contoh — Herewego/Tour_Planning_Sample
| Endpoint | Status audit | Layer |
|----------|-------------|-------|
| [MapServer](https://dev.gisportal.id/server/rest/services/Herewego/Tour_Planning_Sample/MapServer) | ✅ OK | Tour Planning Sample, Depots, Orders, Vehicle |
| [FeatureServer](https://dev.gisportal.id/server/rest/services/Herewego/Tour_Planning_Sample/FeatureServer) | ✅ OK | Depots, Orders, Vehicle + table Sample Vehicle Type |
- Spatial Reference: **EPSG 4326**
- Extent: area Bogor/Jakarta (~106.77–106.86, -6.32 – -6.16)
- Keywords: Here, Here Simulation, Demo, Tour Planning
---
## Published Services (Ringkasan)
| Lokasi | Jumlah |
|--------|--------|
| Root (`/rest/services`) | 8 |
| Folder user (21 folder) | 66 |
| **Total user services** | **~74** |
| System + Utilities | +30 (internal ArcGIS) |
### Folder user
| Folder | Services |
|--------|----------|
| Hosted | 20 |
| Umar | 9 |
| agriculture | 6 |
| mfajri | 6 |
| test_fayra | 4 |
| Training_Experience_Builder | 4 |
| GeonetLocation, Herewego | 2 masing-masing |
| 3D_Drone, electricity, HGDS, Onemap_Cibinong, pajakreklame, researchpublish, RnD, RnD_Gov_Disaster, telekomunikasi, Test, testdiky3, Vidya, water | 1 masing-masing |
### Root services
- `3D_Merapi_Public_WEL` [ImageServer]
- `3D_Merapi_WEL1` [ImageServer]
- `Batas_Kelurahan` [FeatureServer, MapServer]
- `CC_Point_CCTV1` [FeatureServer, MapServer]
- `test_feature_MIL1` [MapServer]
- `Water_MIL1` [MapServer]
---
## Proses & Resource
| Metrik | Nilai |
|--------|-------|
| ArcSOC processes | **76** |
| Total proses ArcGIS (ArcSOC + Server + Java + postgres DS) | **158** |
| Working Set total | **~24 GB** |
| Private Memory total | **~33 GB** |
| RAM server | 128 GB (18,7% terpakai saat audit) |
| Disk C: free | 320 GB (37% terpakai) |
### Java / Portal (co-located)
| Proses | WS | PM |
|--------|----|----|
| java (Portal) | 2,4 GB | 2,4 GB |
| javaw (Server/Portal) | 0,8 + 1,1 + 0,3 GB | ~7,4 GB |
### ArcSOC memory (top)
- ~250–330 MB WS per instance
- Banyak instance start 11:11 dan 12:00 WIB (setelah reboot)
---
## Arsitektur Akses
```text
Internet / LAN
https://dev.gisportal.id/server (NAT → 10.100.1.51:443)
IIS Web Adaptor (/server → ArcGISWebAdaptorAppPoolserver)
https://dev11.gisportal.id:6443 (internal Server HTTPS)
ArcGIS Server (port 6099, service ArcGISServer.exe)
ArcSOC instances (satu atau lebih per published service)
```
**Web Adaptor config:** `C:\inetpub\wwwroot\server\WebAdaptor.config`
- Server URL internal: `https://dev11.gisportal.id:6443`
- HTTP port internal: `6080`
---
## Windows Services ArcGIS
| Service | Status | StartType |
|---------|--------|-----------|
| ArcGIS Server | Running | Automatic |
| Portal for ArcGIS | Running | Automatic |
| ArcGIS Data Store | Running | Automatic |
| ArcGIS Monitor Agent | Running | Automatic |
---
## Port Listening (ArcGIS)
| Port | Proses | Fungsi |
|------|--------|--------|
| 443 | IIS (System) | Web Adaptor HTTPS publik |
| 6080 | javaw | Server HTTP internal |
| 6099 | ArcGISServer | Server admin/REST internal |
| 6443 | javaw | Server HTTPS internal (Web Adaptor target) |
---
## IIS App Pool — Server Web Adaptor
| Setting | Nilai | Catatan |
|---------|-------|---------|
| Name | `ArcGISWebAdaptorAppPoolserver` | |
| State | Started | |
| Idle timeout | `00:00:00` | Disabled (bagus) |
| Periodic restart | **1.05:00:00** (~29 jam) | ⚠ Bisa putus koneksi sementara |
| Private memory limit | 0 (unlimited) | |
---
## Temuan Event Log (7 hari terakhir)
### DCOM Error 10016 — ⚠ Berulang hari ini
```
The application-specific permission settings do not grant Local Activation permission
for COM Server CLSID {D63B10C5-BB46-4990-A94F-E40B9D520160}
to user IIS APPPOOL\ArcGISWebAdaptorAppPoolserver
```
- Terjadi berkali-kali sejak reboot (11:10–11:47 WIB)
- Juga muncul untuk user `DEV11\arcgis`
- **Kemungkinan penyebab intermiten:** Web Adaptor IIS gagal komunikasi COM ke ArcGIS Server component
### Application Error (ArcGIS/ArcSOC)
- Tidak ada entry error ArcGIS di Application log 7 hari terakhir (saat audit)
---
## Log Server
**Path:** `C:\Program Files\ArcGIS\Server\usr\logs\DEV11.GISPORTAL.ID\`
| Lokasi | Isi |
|--------|-----|
| `services\<ServiceName>\` | Log per published service (ArcSOC) |
| `server\` | Log komponen server |
Log terakhir yang terlihat: Jan 2026 (ImageServer 3D_Merapi) — sedikit log baru hari ini, kemungkinan karena baru reboot.
---
## Analisis Issue: Service Tidak Dapat Diakses Setelah Beberapa Jam
### Pola yang cocok dengan gejala
1. **Server baru reboot → semua OK** (terverifikasi audit ini)
2. **76 ArcSOC untuk ~74 service** — instance menumpuk seiring waktu
3. **ArcSOC crash / hang** — proses mati tapi service masih "Started" di Manager
4. **Web Adaptor ↔ Server putus** — DCOM error, port 6443 tidak merespons
5. **IIS App Pool recycle** — setiap ~29 jam, bisa ganggu koneksi aktif
6. **Semua komponen Enterprise di 1 mesin** — Portal + Server + Data Store + Laragon + 74 service = beban tinggi
### Kemungkinan penyebab (prioritas)
| # | Penyebab | Indikator |
|---|----------|-----------|
| 1 | **ArcSOC instance mati/hang** | Service error di REST tapi Windows Service masih Running |
| 2 | **DCOM permission Web Adaptor** | Event 10016 berulang di System log |
| 3 | **Memory leak ArcSOC** | RAM naik gradual; saat ini 24 GB dari 158 proses |
| 4 | **Terlalu banyak published service aktif** | 74 user service + System = 76+ ArcSOC |
| 5 | **Internal HTTPS 6443 gagal** | Web Adaptor tidak reach Server meski 6099 hidup |
| 6 | **Co-located Portal + DS** | Kontention CPU/RAM/I/O setelah uptime panjang |
### Yang BUKAN penyebab (saat audit)
- RAM penuh — masih 104 GB free
- Disk penuh — masih 320 GB free
- Windows Service stopped — semua Running
- Firewall block — REST publik OK dari luar
---
## Langkah Diagnostik Saat Issue Terjadi
Jalankan via SSH ke DEV11:
```powershell
# 1. Cek Windows Service
Get-Service 'ArcGIS Server','Portal for ArcGIS','ArcGIS Data Store'
# 2. Cek port internal
Test-NetConnection localhost -Port 6099
Test-NetConnection localhost -Port 6443
# 3. Hitung ArcSOC
(Get-Process ArcSOC -ErrorAction SilentlyContinue).Count
# 4. Test REST publik vs internal
Invoke-WebRequest 'https://dev.gisportal.id/server/rest/services?f=pjson' -UseBasicParsing
# Internal (dari server, pakai hostname bukan localhost):
Invoke-WebRequest 'https://dev11.gisportal.id/server/rest/services?f=pjson' -UseBasicParsing
# 5. Cek log service spesifik
Get-ChildItem 'C:\Program Files\ArcGIS\Server\usr\logs\DEV11.GISPORTAL.ID\services' -Recurse -Filter '*.log' |
Sort-Object LastWriteTime -Descending | Select-Object -First 5
# 6. Event log DCOM
Get-WinEvent -FilterHashtable @{LogName='System'; Id=10016; StartTime=(Get-Date).AddHours(-24)} -MaxEvents 10
```
### Restart bertingkat (urutan disarankan)
1. **Restart service di ArcGIS Server Manager** (service yang error saja)
2. **Restart Windows Service `ArcGIS Server`** (lebih cepat dari reboot penuh)
3. **Recycle IIS App Pool** `ArcGISWebAdaptorAppPoolserver`
4. **Reboot Windows** (last resort — sudah terbukti fix sementara)
```powershell
Restart-Service 'ArcGIS Server'
Import-Module WebAdministration
Restart-WebAppPool 'ArcGISWebAdaptorAppPoolserver'
```
---
## Rekomendasi Perbaikan
### Segera
- [ ] **Monitor REST endpoint** via Zabbix (agent sudah terpasang port 10050):
- `https://dev.gisportal.id/server/rest/services/Herewego/Tour_Planning_Sample/MapServer?f=pjson`
- `https://dev.gisportal.id/server/rest/services?f=pjson`
- [ ] **Fix DCOM permission** untuk `IIS APPPOOL\ArcGISWebAdaptorAppPoolserver` (Component Services → DCOM Config)
- [ ] **Stop service tidak terpakai** — kurangi beban ArcSOC (74 service di dev server terlalu banyak)
### Jangka menengah
- [ ] **Kurangi min/max instances** per service di Server Manager (khususnya MapServer/ImageServer berat)
- [ ] **Scheduled restart** ArcGIS Server service (mis. mingguan malam hari) sebagai workaround
- [ ] **Pisahkan Portal dan Server** ke VM berbeda (best practice Enterprise)
- [ ] **Aktifkan ArcGIS Monitor** alerts (agent sudah running) untuk service down
- [ ] **Review App Pool recycle** — pertimbangkan disable periodic restart atau set waktu maintenance
### Saat issue terjadi lagi
- [ ] Catat uptime (`systeminfo | find "Boot Time"`) dan jumlah ArcSOC
- [ ] Simpan log dari folder service yang error sebelum restart
- [ ] Bandingkan apakah port 6443 masih listening
---
## Referensi
- Server audit host: `server-audit.md`
- SSH & topology: `server-audit/host.md`, `docs/server.md`
- Web Adaptor IIS path: `/server``C:\inetpub\wwwroot\server`
- Server binaries: `C:\Program Files\ArcGIS\Server\`
- Service configs on disk: `C:\arcgisserver\directories\arcgissystem\arcgisinput\`

109
ArcGIS Enterprise/ArcGIS Base Development/server-audit.md

@ -0,0 +1,109 @@ @@ -0,0 +1,109 @@
# ArcGIS Enterprise — ArcGIS Base Development
**Terakhir diaudit:** 6 Jul 2026
## Akses SSH
```text
ssh dev51-base
ssh Administrator@10.100.1.51 -p 22
```
- Key: `~/.ssh/id_ed25519_geonet_proxmox`
- Login key-only (passwordless) via `C:\ProgramData\ssh\administrators_authorized_keys`
## Identitas Server
| Item | Nilai |
|------|-------|
| Hostname | DEV11 |
| IP | 10.100.1.51 |
| OS | Windows Server 2016 Standard (build 10.0.14393) |
| RAM | 128 GB |
| Disk C: | 511 GB — terpakai ~37% (320 GB free) |
| Hypervisor | Proxmox (Virtio-win drivers terpasang) |
## Stack ArcGIS Enterprise (utama)
| Komponen | Versi | Status |
|----------|-------|--------|
| Portal for ArcGIS | 12.0.58905 | Running |
| ArcGIS Server | 12.0.58905 | Running |
| ArcGIS Data Store (Relational) | 12.0.58905 | Running |
| ArcGIS Web Adaptor (IIS) | 11.4 & 12.0 | Terpasang |
| ArcGIS Pro | 3.5.57366 | Terpasang |
| ArcGIS Insights | 2023.3.0.5972 | Terpasang |
| ArcGIS Monitor | 2023.3.1 | Running (agent) |
**Direktori ArcGIS:** `C:\Program Files\ArcGIS\{Portal,Server,DataStore,Monitor}`
### IIS — Web Adaptor (Default Web Site, port 80/443)
| Path | App Pool |
|------|----------|
| `/portal` | ArcGISWebAdaptorAppPoolportal |
| `/server` | ArcGISWebAdaptorAppPoolserver |
| `/image` | ArcGISWebAdaptorAppPoolimage |
| `/notebook` | ArcGISWebAdaptorAppPoolnotebook |
| `/notebooksvr` | ArcGISWebAdaptorAppPoolnotebooksvr |
| `/workflow` | ArcGISWebAdaptorAppPoolworkflow |
| `/knowledge` | ArcGISWebAdaptorAppPoolknowledge |
| `/geoevent` | ArcGISWebAdaptorAppPoolgeoevent |
| `/exbapp` | DefaultAppPool (Experience Builder) |
### Service ArcGIS yang berjalan
- Portal for ArcGIS
- ArcGIS Server
- ArcGIS Data Store
- ArcGIS Monitor Agent
## Aplikasi Non-Bawaan Windows (lainnya)
### Development
| Aplikasi | Versi | Catatan |
|----------|-------|---------|
| Laragon | 5.0.0 | `C:\laragon` — PHP 7.4, MySQL 5.7, Apache 2.4, Node 14, Redis, Memcached |
| DBeaver | 7.3.3 | DB client |
| SQL Server Management Studio | 18.5.1 | |
| Integration Services (SSIS) | 15.0 | |
| .NET SDK | 8.0.302 | + ASP.NET Core Hosting Bundle 6.0 & 8.0 |
| ODBC Driver 17 / OLE DB Driver | 17.5 / 18.3 | SQL Server connectivity |
| SQL Server 2012 Native Client | 11.4 | |
### Utilitas & Monitoring
| Aplikasi | Versi |
|----------|-------|
| Google Chrome | 149.0.7827.201 |
| Notepad++ | 8.4.2 |
| WinRAR | 5.70 |
| MiniTool Partition Wizard | 12.1.0 |
| Zabbix Agent | 6.2.1 (port 10050) |
| OpenSSH Server | 10.0 (port 22) |
| Microsoft Web Deploy | 4.0 |
## Port Penting
| Port | Proses | Fungsi |
|------|--------|--------|
| 22 | sshd | SSH |
| 80 / 443 | IIS | Web Adaptor |
| 6099 | ArcGISServer | Server internal |
| 7099 | ArcGISPortal | Portal internal |
| 9099 | ArcGISDataStore | Data Store internal |
| 7654, 9876 | postgres | PostgreSQL (Data Store) |
| 10050 | zabbix_agentd | Monitoring |
## Fitur Windows Bawaan (relevan)
- IIS (Web Server, ASP.NET 4.6, Windows Auth, WebSocket)
- .NET Framework 3.5 & 4.6
- Windows Defender, PowerShell 5.1, File Server / SMB
## Catatan
- Server berperan sebagai **ArcGIS Enterprise Base Development** lengkap (Portal + Server + Data Store + Web Adaptor).
- Tambahan di luar docs awal: ArcGIS Pro, Insights, Monitor, dan Laragon (dev stack PHP/MySQL).
- Banyak instance **ArcSOC** aktif — normal untuk ArcGIS Server dengan banyak service/geoprocessing.

3
ArcGIS Enterprise/docs/adr/000-template.md

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
# ADR template
Salin untuk keputusan arsitektur baru.

14
ArcGIS Enterprise/docs/ai-session-summary.md

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
# AI Session Summary
`yaml
session: 2026-07-06-init
project: ArcGIS Enterprise/
version: 0.1.0
status: ready
next: Lengkapi operasional dari server live
`
## TL;DR
- Bootstrap docs AI-Agent-Standards untuk infra ArcGIS Enterprise
- Host utama: 10.100.1.51 (dev.gisportal.id)

6
ArcGIS Enterprise/docs/changelog.md

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
# Changelog — ArcGIS Enterprise
## [0.1.0] - 2026-07-06
### Added
- Struktur dokumentasi AI-Agent-Standards

20
ArcGIS Enterprise/docs/context-index.md

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
# Context Index — ArcGIS Enterprise
## Zona A
| Item | Value |
|------|-------|
| Path | ArcGIS Enterprise/ |
| Cold start | [../AGENTS.md](../AGENTS.md) |
| Versi | 0.1.0 — [version.md](./version.md) |
| Stack | Windows Server, IIS, ArcGIS Enterprise |
| Tipe | infra |
| Last updated | 2026-07-06 |
## Zona B
| Dokumen | Path |
|---------|------|
| Server | [server.md](./server.md) |
| Todo | [todo.md](./todo.md) |
| Migrasi | [guide/migration.md](./guide/migration.md) |

3
ArcGIS Enterprise/docs/guide/README.md

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
# Guides
- [migration.md](./migration.md)

11
ArcGIS Enterprise/docs/guide/migration.md

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
# Migration Guide
## Topology
| Host | Role |
|------|------|
| 10.100.1.51 | Portal, Server, DS Relational, IIS |
| 10.100.1.52 | Data Store Object |
| 10.100.1.53 | Data Store Spatio |
DNS: dev.gisportal.id → 10.100.1.51 (tidak lewat nginx .24)

3
ArcGIS Enterprise/docs/issues.md

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
# Issues
Belum ada issue terdokumentasi.

8
ArcGIS Enterprise/docs/project-status.md

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
# Project Status — ArcGIS Enterprise
ESRI ArcGIS Enterprise dev — Portal, Server, Web Adaptor, Data Stores
| Item | Status |
|------|--------|
| Dokumentasi bootstrap | Done 2026-07-06 |
| Operasional | Lihat server.md |

29
ArcGIS Enterprise/docs/server.md

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
# Server — ArcGIS Enterprise
> **Last updated:** 2026-07-06
## Topology
| Host | IP | Role |
|------|-----|------|
| ArcGIS Base | `10.100.1.51` | Portal, Server, Web Adaptor, DS Relational, IIS |
| DS Object | `10.100.1.52` | Data Store Object |
| DS Spatio | `10.100.1.53` | Data Store Spatio |
## DNS
| Domain | Routing |
|--------|---------|
| `dev.gisportal.id` | Langsung `10.100.1.51`**bypass** nginx `.24` |
| Publik | `117.102.73.100` → NAT `10.100.1.51:443` |
## SSH aliases
```bash
ssh dev51-base # 10.100.1.51
ssh dev13-object # 10.100.1.52 (verify alias di server-audit/host.md)
```
Detail: `ARCHITECTURE.md`, `server-audit/host.md` § ArcGIS.
**Aturan:** perubahan topology → ADR + version.md.

8
ArcGIS Enterprise/docs/system-requirements.md

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
# System Requirements — ArcGIS Enterprise
| Field | Value |
|-------|-------|
| Stack | Windows Server, IIS, ArcGIS Enterprise |
| Host | 10.100.1.51 |
Env: [../.env.example](../.env.example)

9
ArcGIS Enterprise/docs/todo.md

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
# TODO — ArcGIS Enterprise
## High
- [ ] Verifikasi config live vs dokumentasi repo
## Selesai
- [x] Bootstrap AI-Agent-Standards 2026-07-06

6
ArcGIS Enterprise/docs/version.md

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
# Version History
## [0.1.0] - 2026-07-06
- Bootstrap dokumentasi AI-Agent-Standards
- Topology dari ARCHITECTURE.md: .51 base, .52 object DS, .53 spatio DS

6
Dockge/.env.example

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
# Dockge — .env.example
DOCKGE_PRIMARY_URL=https://dockge.gisportal.id
DOCKGE_PRIMARY_HOST=10.100.1.24
DOCKGE_STACKS_PATH=/opt/stacks
DOCKGE_AGENT_MAILCOW_HOST=10.100.1.28
DOCKGE_AGENT_MAILCOW_PORT=5001

33
Dockge/AGENTS.md

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
# AGENTS.md — Cold Start
> Project: Dockge/
> Sesi: [docs/ai-session-summary.md](./docs/ai-session-summary.md)
> Peta: [docs/context-index.md](./docs/context-index.md)
## Apa ini?
Docker stack manager — dockge.gisportal.id; primary .24 + agents remote
## Aturan kritis
- Stack path standar: /opt/stacks/{nama}
- Agent remote: port 5001 (LAN only)
- Deploy stack → append docs/version.md
- No commit secret compose .env
- **No hardcoded URL** — .env.example
- No commit kecuali user minta
- Pengetahuan penting wajib ke docs
## Lanjut kerja
1. AGENTS.md → docs/ai-session-summary.md → STOP
2. docs/server.md, docs/todo.md
## Pointer
| Butuh | Buka |
|-------|------|
| Versi | [docs/version.md](./docs/version.md) |
| Server | [docs/server.md](./docs/server.md) |
| Migrasi | [docs/guide/migration.md](./docs/guide/migration.md) |

3
Dockge/docs/adr/000-template.md

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
# ADR template
Salin untuk keputusan arsitektur baru.

14
Dockge/docs/ai-session-summary.md

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
# AI Session Summary
`yaml
session: 2026-07-06-init
project: Dockge/
version: 1.5.0
status: ready
next: Lengkapi operasional dari server live
`
## TL;DR
- Bootstrap docs AI-Agent-Standards untuk infra Dockge
- Host utama: 10.100.1.24 (dockge.gisportal.id)

6
Dockge/docs/changelog.md

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
# Changelog — Dockge
## [1.5.0] - 2026-07-06
### Added
- Struktur dokumentasi AI-Agent-Standards

20
Dockge/docs/context-index.md

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
# Context Index — Dockge
## Zona A
| Item | Value |
|------|-------|
| Path | Dockge/ |
| Cold start | [../AGENTS.md](../AGENTS.md) |
| Versi | 1.5.0 — [version.md](./version.md) |
| Stack | Dockge v1.5.0, /opt/stacks/ |
| Tipe | infra |
| Last updated | 2026-07-06 |
## Zona B
| Dokumen | Path |
|---------|------|
| Server | [server.md](./server.md) |
| Todo | [todo.md](./todo.md) |
| Migrasi | [guide/migration.md](./guide/migration.md) |

3
Dockge/docs/guide/README.md

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
# Guides
- [migration.md](./migration.md)

12
Dockge/docs/guide/migration.md

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
# Migration Guide
## Stacks
Semua stack Docker production di /opt/stacks/ pada host terkait.
| Host | Role |
|------|------|
| 10.100.1.24 | Dockge primary + mayoritas stack |
| 10.100.1.28 | Dockge agent (Mailcow) |
Migrasi stack: export compose + volume backup → deploy di host baru → update nginx.

3
Dockge/docs/issues.md

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
# Issues
Belum ada issue terdokumentasi.

8
Dockge/docs/project-status.md

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
# Project Status — Dockge
Docker stack manager — dockge.gisportal.id; primary .24 + agents remote
| Item | Status |
|------|--------|
| Dokumentasi bootstrap | Done 2026-07-06 |
| Operasional | Lihat server.md |

45
Dockge/docs/server.md

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
# Server — Dockge
> **Last updated:** 2026-07-06
## Primary (`.24`)
| Item | Nilai |
|------|-------|
| URL | `https://dockge.gisportal.id` |
| Host | `10.100.1.24` |
| Stacks path | `/opt/stacks/` |
| Versi | Dockge v1.5.0 |
## Remote agent (Mailcow `.28`)
| Item | Nilai |
|------|-------|
| Agent URL | `http://10.100.1.28:5001` |
| Stack | `/opt/stacks/mailcow` |
| Firewall | port 5001 dari `.24` saja |
---
## Stacks utama (`/opt/stacks/`)
| Stack | Host | Repo docs |
|-------|------|-----------|
| geonet-console | `.24` | server-connection |
| zammad | `.24` | Zammad |
| mailcow | `.28` | Mailcow |
| geonet-collector | `.24` | GeoNetAgent |
| databaselist | `.24` | server-connection |
Setiap deploy stack → append [version.md](./version.md) di repo terkait.
---
## SSH
```bash
ssh reverse-proxy # .24 primary
ssh mailcow # .28 agent
```
Credential: env server — jangan di docs.

8
Dockge/docs/system-requirements.md

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
# System Requirements — Dockge
| Field | Value |
|-------|-------|
| Stack | Dockge v1.5.0, /opt/stacks/ |
| Host | 10.100.1.24 |
Env: [../.env.example](../.env.example)

9
Dockge/docs/todo.md

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
# TODO — Dockge
## High
- [ ] Verifikasi config live vs dokumentasi repo
## Selesai
- [x] Bootstrap AI-Agent-Standards 2026-07-06

10
Dockge/docs/version.md

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
# Version History
## [1.5.0] - 2026-07-06
- Bootstrap dokumentasi AI-Agent-Standards
- Primary Dockge di .24; agent Mailcow .28:5001
## [1.5.0-deploy] - 2026-07-03
- Dockge agent terpasang di VM Mailcow

103
GeoNetAgent/AGENTS.md

@ -1,84 +1,41 @@ @@ -1,84 +1,41 @@
# AGENTS.md — AI Agent Rules
# AGENTS.md — Cold Start
## Entry Point Wajib
> Project: `GeoNetAgent/`
> Sesi terakhir: [docs/ai-session-summary.md](./docs/ai-session-summary.md)
> Peta lengkap: [docs/context-index.md](./docs/context-index.md)
> Env template: [.env.example](./.env.example)
Sebelum melakukan pekerjaan apa pun, baca berurutan:
1. `docs/context-index.md` — peta seluruh dokumentasi
2. `.cursor/blueprint.md` — visi, fase, batas modul
3. `.cursor/progress.md` — fase aktif & checklist DoD
4. Dokumen spesifik modul (db-schema, api-contracts, dll)
## Apa ini?
**Jangan implement fase N+1 jika fase N belum DoD.**
IT asset monitoring & CMDB ringan: Windows PowerShell agent → FastAPI collector → PostgreSQL. Melengkapi Zabbix.
---
## Aturan kritis
## Aturan Kode
- **No hardcoded URL/domain/IP** — baca dari `.env` / `.env.example`
- Jangan commit `.env`; credentials di server saja
- No commit kecuali user minta eksplisit
- **Deploy agent:** bump `agent/VERSION` + append [docs/version.md](./docs/version.md) + publish script
- Jangan implement fase N+1 jika fase N belum DoD (`.cursor/progress.md`)
- SSH butuh approval user; SCP pakai `-o "MACs=hmac-sha1"` dari Windows
- Pengetahuan penting **wajib ke docs**, bukan chat
### Python (FastAPI Collector)
- Gunakan `snake_case` untuk variable, function, file
- Type hints wajib di semua function signature
- Pydantic schema di `app/schemas/` — jangan inline model di routes
- Route di `app/api/` — satu file per resource
- DB session via dependency injection (`get_db`)
- Jangan commit `.env` — gunakan `.env.example`
## Lanjut kerja
### PowerShell (Agent)
- Module di `agent/lib/` — satu file per sensor (Get-CpuInfo.ps1, dll)
- Jangan hardcode credential di skrip
- Hindari literal `$env:` di file `.ps1` yang di-serve Nginx (nginx potong respons)
- Test lokal dulu sebelum publish ke `agent.gisportal.id/install/`
### SQL / Database
- PK: UUID (`gen_random_uuid()`)
- Timestamp: `timestamptz`
- Nama tabel: `snake_case` plural
- Setiap perubahan schema → update `docs/database.md` + `.cursor/db-schema.md`
- Migration order: lihat `infra/db/README.md`
---
## Aturan Deploy
**Wajib setelah bump versi agent:**
```powershell
.\infra\collector\publish_agent_install.ps1 # upload ke agent.gisportal.id/install/
.\infra\collector\push_collector.ps1 # rebuild UI/API jika berubah
```
1. AGENTS.md
2. docs/ai-session-summary.md
3. STOP
4. On-demand: docs/todo.md, docs/issues.md, .cursor/blueprint.md, docs/server.md
```
- Update `agent/VERSION` + `agent/install/manifest.json` sebelum publish
- Update `Riwayat milestone` di `.cursor/progress.md`
- Update `Sync terakhir` di `perintah.md`
**File transfer ke server:** gunakan `scp -o "MACs=hmac-sha1"` dari Windows.
Jangan heredoc SSH dari PowerShell — `$` variabel terpotong.
---
## Aturan Dokumentasi
- Setiap perubahan arsitektur → buat ADR baru di `docs/adr/ADR-00N-*.md`
- ADR **tidak boleh dihapus** — buat ADR baru jika keputusan berubah
- Setelah implementasi: update `.cursor/progress.md` + `perintah.md`
- Implementasi tanpa dokumentasi = **belum selesai**
- Update `docs/context-index.md` jika ada dokumen baru
---
## Aturan Keamanan
- **Jangan commit secret**: password DB, bearer token, API key
- Credentials DB ada di `/root/geonetagent_dev.credentials` di server 10.100.1.25
- Token agent ada di `.env` server — lihat `docs/environment.md` untuk nama variabelnya
- SSH command butuh approval user sebelum dijalankan
- Nginx `agent.gisportal.id`: `ssi off` — jangan taruh `$env:` literal di file `.ps1` yang di-serve
---
Detail kode: `.cursor/rules` equivalent via `.cursor/blueprint.md`, `docs/development.md`.
## Batas Sistem (Non-goals)
## Pointer cepat
Jangan implement hal-hal berikut — sudah diputuskan di ADR/blueprint:
- ❌ Engine monitoring time-series (pakai Zabbix)
- ❌ NMS pengganti MikroTik/Proxmox tools
- ❌ Duplikasi data GIS legacy dari SQL Server
- ❌ Remote desktop / RMM agent
- ❌ Ticketing enterprise penuh (fase awal)
| Butuh | Buka |
|-------|------|
| Priority | [docs/todo.md](./docs/todo.md) |
| Versi / deploy | [docs/version.md](./docs/version.md) |
| Server | [docs/server.md](./docs/server.md) |
| Schema DB | [docs/database.md](./docs/database.md) |
| ADR | [docs/adr/](./docs/adr/) |

25
GeoNetAgent/docs/ai-session-summary.md

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
# AI Session Summary
```yaml
session: 2026-07-06-docs-bootstrap
project: GeoNetAgent/
version: 0.1.12-phase0
status: ready
next: Lanjutkan fase aktif di .cursor/progress.md
agent: Cursor
```
## TL;DR
- Bootstrap docs AI-Agent-Standards: version.md, system-requirements.md, guide/, ai-session-summary
- Agent live `v0.1.12-phase0`; collector di `10.100.1.24`
## Lanjut dari sini
1. Baca `.cursor/progress.md` untuk fase aktif
2. Bump versi agent → append `docs/version.md` + publish
## Jangan
- Jangan hardcode credential di skrip PowerShell
- Jangan commit `.env`

21
GeoNetAgent/docs/changelog.md

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
# Changelog — GeoNetAgent
Ringkasan per versi: [version.md](./version.md).
---
## [Unreleased]
---
## [0.1.12-phase0] - 2026-06-29
### Changed
- Agent sensors & manifest update
---
## [0.1.0] - 2026-07-06
### Added
- Bootstrap AI-Agent-Standards (version.md, system-requirements.md, guide/)

5
GeoNetAgent/docs/context-index.md

@ -1,7 +1,8 @@ @@ -1,7 +1,8 @@
# Context Index — GeoNetAgent ⭐ AI Entry Point
> **Terakhir Diperbarui:** 2026-06-29
> **Versi Agent:** v0.1.10-phase0
> **Terakhir Diperbarui:** 2026-07-06
> **Versi Agent:** v0.1.12-phase0 — [version.md](./version.md)
> **Cold start:** [../AGENTS.md](../AGENTS.md)
> **Fase Aktif:** Fase 1 — Foundation 🔄
---

6
GeoNetAgent/docs/guide/README.md

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
# Guides — GeoNetAgent
| Guide | Path | Kapan |
|-------|------|-------|
| Migrasi server/domain | [migration.md](./migration.md) | Pindah collector/nginx |
| Deploy | [../deployment.md](../deployment.md) | Install stack |

19
GeoNetAgent/docs/guide/migration.md

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
# Migration Guide — GeoNetAgent
Inventori: [../server.md](../server.md). Env: [../../.env.example](../../.env.example).
## Host saat ini
| Komponen | Host |
|----------|------|
| Collector + nginx | `10.100.1.24` |
| PostgreSQL | `10.100.1.25` |
| Agent install URL | `agent.gisportal.id` |
## Checklist migrasi
1. Deploy collector ke host baru
2. Update DNS/nginx → `collector.gisportal.id`, `agent.gisportal.id`
3. Update `.env` + `agent/install` manifest URL
4. Re-publish agent (`publish_agent_install.ps1`)
5. Append [../version.md](../version.md)

26
GeoNetAgent/docs/system-requirements.md

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
# System Requirements — GeoNetAgent
| Field | Value |
|-------|-------|
| Project | `GeoNetAgent/` |
| Tipe | app (agent + API) |
| Last updated | 2026-07-06 |
## Runtime
| Tool | Minimum | Dipakai |
|------|---------|---------|
| Python | 3.11+ | FastAPI collector |
| PostgreSQL | 15+ | `10.100.1.25` |
| Docker | 24+ | Collector stack `.24` |
| PowerShell | 5.1+ | Windows agent |
| nginx | 1.24+ | `agent.gisportal.id` |
## Variabel env (no hardcode)
| Variabel | Contoh | File |
|----------|--------|------|
| `COLLECTOR_API_URL` | `https://collector.gisportal.id` | `.env.example` |
| `DATABASE_URL` | PostgreSQL `.25` | collector `.env` |
Detail: [environment.md](./environment.md), [server.md](./server.md).

23
GeoNetAgent/docs/version.md

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
# Version History
**Owner:** riwayat versi deploy GeoNetAgent. Append per perubahan material.
---
## [0.1.12-phase0] - 2026-06-29
- Agent version bump (lihat `agent/VERSION`)
- Collector/nginx di `10.100.1.24`
---
## [0.1.0] - 2026-06-15
- Bootstrap dokumentasi AI-Agent-Standards (version.md, guide/, system-requirements.md)
- Agent phase0 foundation
---
## [0.1.0-docs] - 2026-06-01
- Inisialisasi dokumentasi repo (`docs/`, ADR-001–003)

2
GeoNetAgent/infra/nginx/geonetagent-collector-public.conf

@ -66,6 +66,6 @@ server { @@ -66,6 +66,6 @@ server {
location / {
default_type application/json;
return 404 '{"error":{"code":"NOT_FOUND","message":"Not found"}}';
return 404 "{\"error\":{\"code\":\"NOT_FOUND\",\"message\":\"Not found\"}}";
}
}

2
Mailcow/.env.example

@ -33,7 +33,7 @@ MAILCOW_SUPPORT_PASSWORD= @@ -33,7 +33,7 @@ MAILCOW_SUPPORT_PASSWORD=
# Domain email
# =============================================================================
MAILCOW_DOMAIN_PRIMARY=geonet.co.id
MAILCOW_DOMAIN_SECONDARY=gegeo.my.id
MAILCOW_DOMAIN_SECONDARY=gisportal.id
# =============================================================================
# Port layanan (default Mailcow — jangan ubah kecuali ada konflik)

58
Mailcow/AGENTS.md

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
# AGENTS.md — Cold Start
> Project: `Mailcow/`
> Sesi terakhir: [docs/ai-session-summary.md](./docs/ai-session-summary.md)
> Peta lengkap: [docs/context-index.md](./docs/context-index.md)
> Env template: [.env.example](./.env.example)
## Apa ini?
Self-hosted mail server (Mailcow dockerized) untuk `geonet.co.id` + `gisportal.id`. Inbox canonical di `sync.geonet.co.id`; POP3 drain cPanel (geonet); `gisportal.id` langsung ke Mailcow tanpa cPanel.
## Aturan kritis
- **No hardcoded URL/domain/IP** — baca dari `.env` / `docs/environment.md`
- Copy `.env.example``.env` lokal; **jangan commit** `.env`
- No commit kecuali user minta eksplisit
- **Deploy / rilis:** append [docs/version.md](./docs/version.md) (poin perubahan) + [docs/changelog.md](./docs/changelog.md)
- `support@geonet.co.id` = **authsource ldap** (password AD); fetchmail POP3 tetap dari hosting
- SSH ke server **wajib persetujuan user** (`ssh mailcow` → `10.100.1.28`)
- Pengetahuan penting **wajib ke docs**, bukan hanya chat
## Lanjut kerja (urutan baca)
```
1. AGENTS.md <- Anda di sini
2. docs/ai-session-summary.md <- TL;DR + Lanjut + Jangan
3. STOP - mulai kerja
4. On-demand:
- docs/todo.md
- docs/issues.md
- docs/guide/outlook-imap-setup.md
- docs/guide/migration.md
- docs/system-requirements.md
- docs/server.md
- .env.example
- docs/context-index.md
```
**Rolling session memory = hanya** `docs/ai-session-summary.md`.
## Pointer cepat
| Butuh | Buka |
|-------|------|
| Priority | [docs/todo.md](./docs/todo.md) |
| Versi / deploy | [docs/version.md](./docs/version.md) |
| Bug / RCA | [docs/issues.md](./docs/issues.md) |
| IMAP client (Outlook) | [docs/guide/outlook-imap-setup.md](./docs/guide/outlook-imap-setup.md) |
| Migrasi MX/domain | [docs/guide/migration.md](./docs/guide/migration.md) |
| Multi-domain gisportal.id | [docs/notes/gisportal-id-mail.md](./docs/notes/gisportal-id-mail.md) |
| Hybrid cPanel geonet | [docs/notes/hybrid-migration-cpanel-mailcow.md](./docs/notes/hybrid-migration-cpanel-mailcow.md) |
| System requirements | [docs/system-requirements.md](./docs/system-requirements.md) |
| Server / infra | [docs/server.md](./docs/server.md) |
| Env vars | [docs/environment.md](./docs/environment.md) |
## Multi-project
`Workspace-Context.md``AGENTS.md``docs/ai-session-summary.md`

25
Mailcow/docs/adr/000-template.md

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
# ADR-000: Template
> Salin file ini sebagai `ADR-NNN-judul-keputusan.md` saat ada keputusan arsitektur baru.
## Status
Proposed | Accepted | Deprecated | Superseded
## Konteks
{Masalah atau kebutuhan}
## Keputusan
{Apa yang diputuskan}
## Konsekuensi
- Positif: ...
- Negatif: ...
- Risiko: ...
## Referensi
- [context-index.md](../context-index.md)

29
Mailcow/docs/adr/ADR-0001-support-ldap-auth.md

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
# ADR-0001: support@ authsource LDAP
## Status
Accepted — 2026-07-06
## Konteks
- `support@geonet.co.id` perlu diakses user via IMAP dengan **password AD**
- Inbound email SSOT masih cPanel (MX) → fetchmail POP3 drain ke Mailcow
- Mailbox sebelumnya `authsource=mailcow` (service account SQL)
## Keputusan
**Satu mailbox** `support@geonet.co.id` dengan `authsource=ldap`:
- fetchmail tetap deliver ke alamat yang sama (tidak terpengaruh auth)
- User PC: IMAP `sync.geonet.co.id:993` + password AD
- Tidak membuat mailbox kedua; tidak copy/move inbox
## Konsekuensi
- Positif: satu password AD untuk user; inbox tetap canonical di Mailcow
- Perhatian: SOGo perlu verifikasi setelah switch (`_sogo_static_view`)
- fetchmail POP3 password tetap password **cPanel** (terpisah dari LDAP)
- Zammad channel: gunakan password AD atau app password (dokumentasikan di Zammad)
## Implementasi
Script: `scripts/switch-support-to-ldap.sh`

BIN
Mailcow/docs/ai-session-summary.md

Binary file not shown.

41
Mailcow/docs/changelog.md

@ -1,15 +1,44 @@ @@ -1,15 +1,44 @@
# Changelog — Mailcow
Format [Keep a Changelog](https://keepachangelog.com/). Ringkasan: [version.md](./version.md).
---
## [1.2.5] - 2026-07-06
### Changed
- SPF `gisportal.id` di Master Web: `v=spf1 a mx ~all``v=spf1 ip4:117.102.73.102 -all`
### Verified
- Propagasi SPF di `dns1.masterweb.com` OK
---
## 2026-07-03 — fetchmail, SOGo, outbound relay
## [1.2.4] - 2026-07-06
**Ringkasan:** Email flow support@geonet.co.id end-to-end via Mailcow.
### Added
- Domain `gisportal.id` (Mailcow murni) + `admin@gisportal.id`
- `docs/notes/gisportal-id-mail.md`
- Scripts: `fix-sogo-domain.sh`, `setup-gisportal-inbound-whitelist.sh`
**Fitur:** fetchmail POP3 drain; relay cPanel :587; SOGo operational.
### Fixed
- SOGo Unauthorized (`_sogo_static_view`); inbound forwarding hosts whitelist
**Fix:** SOGo Unauthorized; Gmail 550 smarthost.
---
## [1.2.3] - 2026-07-06
### Added
- Transport map Jalur B `rbsetiawan@` → cPanel `:587`
- `scripts/setup-transport-cpanel-user.sh`
### Verified
- `support@``rbsetiawan@` (cPanel) OK
---
**File:** scripts/install-fetchmail-support.sh, docs/*
## Planned
**Author:** AI Agent (Cursor)
- DKIM `gisportal.id` + `geonet.co.id`
- Zammad IMAP/SMTP channel
- MX cutover geonet.co.id

191
Mailcow/docs/context-index.md

@ -1,149 +1,88 @@ @@ -1,149 +1,88 @@
# Context Index — Mailcow ⭐ AI Entry Point
# Context Index — Mailcow
> **Terakhir Diperbarui:** 2026-07-03
> **Fase Aktif:** Fase 1 — Mailcow live, transisi MX cPanel → sync 📧
**Sesi terakhir:** [docs/ai-session-summary.md](./ai-session-summary.md)
> Cold start: [../AGENTS.md](../AGENTS.md) → [ai-session-summary.md](./ai-session-summary.md) → **STOP**
---
## Filosofi
Proyek ini menerapkan **AI Native Development**:
- Repository = sumber kebenaran utama (Single Source of Truth)
- AI tidak boleh mengandalkan riwayat percakapan
- Developer sering berpindah perangkat — konteks harus tersimpan di repo
- Implementasi tanpa dokumentasi = belum selesai
## Zona A — Always-read pointers
| Item | Value |
|------|-------|
| Nama | Mailcow |
| Path monorepo | `Mailcow/` |
| Cold start | [../AGENTS.md](../AGENTS.md) |
| Sesi terakhir | [ai-session-summary.md](./ai-session-summary.md) |
| Priority | [todo.md](./todo.md) |
| Server / infra | [server.md](./server.md) |
| System requirements | [system-requirements.md](./system-requirements.md) |
| Env template | [../.env.example](../.env.example) |
| Versi | **1.2.5** — [version.md](./version.md) |
| Stack | Mailcow dockerized, fetchmail (geonet), Dovecot/Postfix |
| Domains | `geonet.co.id` (hybrid cPanel), `gisportal.id` (murni) |
| Tipe | infra |
| Last updated | 2026-07-06 |
**Rolling session memory:** hanya [ai-session-summary.md](./ai-session-summary.md).
---
## Urutan Membaca
## Zona B — On-demand map
1. **File ini** — peta navigasi
2. **`docs/ai-session-summary.md`** — ringkasan sesi terakhir ⭐
3. `docs/environment.md` — variabel `.env` (tanpa secret)
3. `.env.example` → copy ke `.env` — isi password & secret
4. Dokumen spesifik task (lihat tabel di bawah)
5. `Zammad/docs/context-index.md` — integrasi email channel
---
### Dokumentasi inti
## Posisi dalam Super Apps
| Dokumen | Kapan baca |
|---------|------------|
| [project-status.md](./project-status.md) | Status milestone |
| [issues.md](./issues.md) | Bug / RCA |
| [changelog.md](./changelog.md) | Audit riwayat |
| [version.md](./version.md) | Deploy / rilis |
| [environment.md](./environment.md) | Variabel env |
| [deployment.md](./deployment.md) | Install stack |
| [guide/](./guide/) | IMAP user, migrasi MX |
```
Internet → MX geonet.co.id → cPanel (staging, inbox kosong setelah POP3)
fetchmail POP3 + delete (VM .27)
Mailcow sync.geonet.co.id — inbox canonical (IMAP)
│ │
Outlook user Zammad
IMAP+SMTP sync IMAP+SMTP sync
```
### Notes (domain)
| Repo terkait | Entry point | Kapan baca |
|--------------|-------------|------------|
| **Zammad** | `Zammad/docs/context-index.md` | Channel email inbound/outbound |
| **server-connection** | `server-connection/docs/context-index.md` | LDAP AD, nginx pattern |
| **mysupperapps_FE_PWA** | `docs/context-index.md` | Mail UI `/mail` (fase berikutnya) |
| Dokumen | Isi |
|---------|-----|
| [notes/gisportal-id-mail.md](./notes/gisportal-id-mail.md) | Domain `gisportal.id` — Mailcow murni, DNS Master Web |
| [notes/hybrid-migration-cpanel-mailcow.md](./notes/hybrid-migration-cpanel-mailcow.md) | Jalur A/B hybrid geonet |
| [notes/ldap-ad-mailcow.md](./notes/ldap-ad-mailcow.md) | LDAP AD + SOGo troubleshooting |
| [notes/storage-nas-qnap.md](./notes/storage-nas-qnap.md) | Backup QNAP |
| [notes/deployment-sizing.md](./notes/deployment-sizing.md) | Sizing VM |
---
### Parent / sibling
## Peta Dokumentasi
### `docs/` — Flat Files
| File | Isi | Status | Baca Saat |
|------|-----|--------|-----------|
| `docs/context-index.md` | **File ini** — navigasi | ✅ | Selalu |
| `docs/environment.md` | Env vars, port, integrasi Zammad | ✅ | Setup, deploy |
| `docs/project-status.md` | Status modul, milestone | ✅ | Planning |
| `docs/ai-session-summary.md` | **Ringkasan sesi terakhir** | ✅ | Lanjut kerja |
| `docs/roadmap.md` | Fase Mailcow dalam super apps | 📋 | Planning |
| `docs/todo.md` | Next actions berprioritas | ✅ | Sebelum coding |
| `docs/changelog.md` | Riwayat perubahan penting | ✅ | Referensi |
| `docs/issues.md` | Known bugs + workaround | ✅ | Debug |
| `docs/server.md` | Server, SSH, spec VM `.28` | ✅ | Deploy |
| `docs/deployment.md` | Step-by-step install Mailcow | ✅ | Deploy |
| `docs/security.md` | Credential, DNS, port 25 | 📋 | Security |
### `docs/notes/`
| File | Isi |
|------|-----|
| `email-sync-migration-concept.md` | **Konsep sync hosting → Mailcow → Zammad** (MX tetap cPanel) |
| `zammad-email-channel.md` | Step-by-step SMTP + IMAP Zammad ↔ Mailcow |
| `dns-cutover-geonet-co-id.md` | MX, SPF, DKIM, DMARC cutover dari cPanel |
| `storage-nas-qnap.md` | **Email di NAS QNAP** — iSCSI / NFS / backup |
| `deployment-sizing.md` | **Linux + Docker, spec VM 30+ user** |
| `ldap-ad-mailcow.md` | **LDAP AD → Mailcow** — auth + provisioning user terpusat |
### Root repo
| File | Isi |
|------|-----|
| `.env.example` | Template konfigurasi — **aman di-commit** |
| `.env` | Nilai aktual (password) — **gitignored** |
| `.gitignore` | Exclude secret & data Mailcow |
| `scripts/install-fetchmail-support.sh` | Install POP3 drain + systemd timer |
| Repo | Entry |
|------|-------|
| Zammad | `Zammad/docs/context-index.md` |
| server-connection | `server-connection/docs/context-index.md` |
| nginx | `nginx/docs/context-index.md` |
| Dockge | `Dockge/docs/context-index.md` |
---
## Quick Reference
## Arsitektur singkat
| Fungsi | URL / Path |
|--------|------------|
| Mailcow UI (target) | `https://sync.geonet.co.id` |
| Hostname mail | `sync.geonet.co.id` |
| Mailbox support (fase sync) | `support@sync.geonet.co.id` |
| Mailbox produksi (hosting) | `support@geonet.co.id` @ cPanel |
| Domain utama | `geonet.co.id` (MX tetap hosting) |
| Docker stack (target) | `/opt/stacks/mailcow/` |
| Server Mailcow | `10.100.1.28` (16 vCPU, 32 GB, 2×512 GB SSD) |
| Server Zammad | `10.100.1.24` |
### Port layanan
| Port | Layanan |
|------|---------|
| 25 | SMTP inbound |
| 465 | SMTPS |
| 587 | Submission (Zammad outbound) |
| 993 | IMAPS (Zammad inbound) |
| 443 | HTTPS Web UI |
### SSH
```bash
ssh mailcow # 10.100.1.28
```
geonet.co.id → MX cPanel → fetchmail POP3 → Mailcow (hybrid, transport maps)
gisportal.id → MX mail.gisportal.id → 117.102.73.102 → Mailcow (murni)
├── Outlook IMAP :993 / SMTP :587 (sync.geonet.co.id)
└── Zammad IMAP (planned)
```
> SSH ke server **wajib minta persetujuan user** sebelum dijalankan.
---
## Status saat ini (2026-07-03)
| Item | Status |
|------|--------|
| Mailcow stack `/opt/stacks/mailcow` | ✅ |
| Domain `geonet.co.id` + DKIM generated | ✅ |
| fetchmail POP3 drain `support@` | ✅ timer 2 menit |
| Outbound relay cPanel `:587` | ✅ transisi |
| SOGo kirim/terima `support@` | ✅ verified user |
| LDAP IdP (user AD lain) | ✅ |
| Zammad → Mailcow channel | ❌ |
| DKIM/SPF DNS publish | ❌ |
| MX cutover ke sync | ❌ |
Detail: [project-status.md](./project-status.md)
| Fungsi | URL / Host |
|--------|------------|
| Mailcow UI | `https://sync.geonet.co.id` |
| VM | `10.100.1.28` |
| IMAP/SMTP | `sync.geonet.co.id` :993 / :587 |
| DNS gisportal | Master Web (`dns1.masterweb.com`) |
---
## Aturan Utama AI
## Aturan kritis AI
1. Baca `docs/context-index.md` ini terlebih dahulu
2. **Jangan commit** `.env` — hanya update `.env.example` (placeholder) dan `docs/environment.md`
3. Integrasi Zammad → referensi `Zammad/docs/` — jangan duplikasi panjang
4. LDAP umum → referensi `server-connection/docs/environment.md`
5. Setelah deploy → buat/update `docs/deployment.md` + `docs/project-status.md`
- No commit `.env`; update `.env.example` + `environment.md`
- Deploy material → append `version.md` + `changelog.md`
- `support@` = authsource **ldap**; `admin@gisportal.id` = **mailcow**
- Jangan masukkan user ke `Mail-Users` jika masih baca cPanel
- SSH wajib persetujuan user

22
Mailcow/docs/environment.md

@ -42,8 +42,8 @@ @@ -42,8 +42,8 @@
| Variable | Contoh | Keterangan |
|----------|--------|------------|
| `MAILCOW_DOMAIN_PRIMARY` | `geonet.co.id` | Domain utama |
| `MAILCOW_DOMAIN_SECONDARY` | `gegeo.my.id` | Domain tambahan (multi-domain) |
| `MAILCOW_DOMAIN_PRIMARY` | `geonet.co.id` | Domain hybrid (MX masih cPanel, transisi Mailcow) |
| `MAILCOW_DOMAIN_SECONDARY` | `gisportal.id` | Domain Mailcow murni — MX langsung ke `117.102.73.102`, **tanpa cPanel** |
---
@ -131,12 +131,22 @@ Error historis DNS container: `Zammad/docs/notes/email-geonet-co-id.md` @@ -131,12 +131,22 @@ Error historis DNS container: `Zammad/docs/notes/email-geonet-co-id.md`
## DNS (panel domain — bukan env Mailcow)
### gisportal.id (Mailcow murni, tanpa cPanel)
| Record | Nilai |
|--------|-------|
| MX | `10 mail.gisportal.id` |
| A `mail.gisportal.id` | `117.102.73.102` |
| SPF | `v=spf1 ip4:117.102.73.102 -all` |
| DKIM | dari Mailcow Admin → domain `gisportal.id` |
### geonet.co.id (hybrid, transisi)
| Variable | Contoh | Record |
|----------|--------|--------|
| `DNS_MX_HOST` | `mail.gisportal.id` | MX → IP publik server |
| `DNS_SPF_INCLUDE` | `include:mail.gisportal.id` | TXT SPF |
| — | *(dari Mailcow UI)* | TXT DKIM |
| — | | TXT DMARC |
| `DNS_MX_HOST` | `sync.geonet.co.id` | MX setelah cutover |
| SPF | | tambah `ip4:117.102.73.102` saat cutover |
| DKIM | | dari Mailcow UI |
---

14
Mailcow/docs/guide/README.md

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
# Guides — Mailcow
**Owner:** runbook operasional Mailcow.
## Indeks
| Guide | Path | Kapan dipakai |
|-------|------|---------------|
| IMAP/SMTP Outlook user | [outlook-imap-setup.md](./outlook-imap-setup.md) | Setup email di PC user |
| Migrasi MX / domain | [migration.md](./migration.md) | Cutover cPanel → Mailcow |
| Deploy Mailcow | [../deployment.md](../deployment.md) | Install / reinstall stack |
| Email sync concept | [../notes/email-sync-migration-concept.md](../notes/email-sync-migration-concept.md) | Arsitektur POP3 drain |
| Zammad channel | [../notes/zammad-email-channel.md](../notes/zammad-email-channel.md) | Integrasi ticketing |
| LDAP AD | [../notes/ldap-ad-mailcow.md](../notes/ldap-ad-mailcow.md) | User AD → mailbox |

91
Mailcow/docs/guide/migration.md

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
# Migration Guide — Mailcow (MX & Domain)
**Owner:** cutover email `geonet.co.id` dari cPanel ke Mailcow.
Inventori: [../server.md](../server.md). Env: [../../.env.example](../../.env.example).
**Prinsip:** URL/domain dynamic via `.env` — bukan hardcode di script.
---
## Status migrasi saat ini (2026-07-06)
| Fase | Status | Keterangan |
|------|--------|------------|
| Mailcow live | ✅ | VM `10.100.1.28`, `sync.geonet.co.id` |
| POP3 drain | ✅ | fetchmail timer 2 menit, `nokeep` |
| Outbound relay | ✅ Transisi | cPanel SMTP :587 |
| MX DNS | ❌ | Masih cPanel |
| DKIM/SPF publish | ❌ | Key generated, belum DNS |
| fetchmail mati | ❌ | Setelah MX cutover |
---
## Inventori sebelum cutover MX
| Item | Saat ini | Target | File update |
|------|----------|--------|-------------|
| MX record | cPanel | `sync.geonet.co.id` | DNS panel |
| Inbox canonical | Mailcow (via POP3) | Mailcow (langsung) | — |
| SPF | cPanel IP | Mailcow `117.102.73.102` | DNS |
| DKIM | — | Mailcow selector | DNS |
| PTR | TBD | `sync.geonet.co.id` | Provider IP |
| fetchmail | aktif | **stop** | systemd disable |
| relay cPanel | aktif | opsional mati | Mailcow relayhosts |
---
## Fase 1 — Transisi (saat ini)
1. MX tetap cPanel
2. fetchmail POP3 drain → Mailcow inbox
3. User Outlook → IMAP `sync.geonet.co.id:993` saja
4. Outbound relay cPanel sampai reputasi IP Mailcow OK
Verifikasi fetchmail:
```bash
ssh mailcow
systemctl status fetchmail-support.timer
tail -20 /var/log/fetchmail-support.log
```
---
## Fase 2 — DNS email publish
1. [ ] Ambil DKIM public key: Mailcow Admin → Configuration → DKIM
2. [ ] Publish TXT DKIM di DNS `geonet.co.id`
3. [ ] Update SPF: `v=spf1 ip4:117.102.73.102 include:... -all`
4. [ ] Tambah DMARC (opsional): `_dmarc.geonet.co.id`
5. [ ] PTR `117.102.73.102``sync.geonet.co.id`
6. [ ] Smoke test: kirim ke Gmail, cek header SPF/DKIM pass
Append [../version.md](../version.md) setelah publish.
---
## Fase 3 — MX cutover
1. [ ] Stabil minimal 2–4 minggu (fetchmail + relay tanpa error)
2. [ ] Turunkan TTL MX 24 jam sebelumnya
3. [ ] Ubah MX → `sync.geonet.co.id` (priority 10)
4. [ ] Monitor Postfix log Mailcow 24–48 jam
5. [ ] `systemctl disable --now fetchmail-support.timer`
6. [ ] Evaluasi matikan relay cPanel (kirim langsung dari Mailcow)
7. [ ] Update docs: server.md, project-status.md, version.md
---
## Rollback MX
1. Kembalikan MX ke cPanel
2. Enable fetchmail timer jika masih diperlukan
3. Catat di [../issues.md](../issues.md)
---
## Riwayat migrasi
| Tanggal | Dari | Ke | Versi | Catatan |
|---------|------|-----|-------|---------|
| 2026-07-03 | — | POP3 drain live | 1.0.0 | fetchmail + relay |
| 2026-07-06 | — | Docs bootstrap | 1.1.0 | SSOT AI-Agent-Standards |

77
Mailcow/docs/guide/outlook-imap-setup.md

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
# Guide — Outlook IMAP/SMTP ke Mailcow
**Untuk:** PC user yang memakai `support@geonet.co.id` (atau mailbox `@geonet.co.id` lain).
**Server:** Mailcow `sync.geonet.co.id`**bukan** `mail.geonet.co.id` (cPanel).
---
## Pengaturan
| Setting | Nilai |
|---------|-------|
| Email / Username | `support@geonet.co.id` |
| Password | Password **Active Directory** (Windows login `support` / `support@geonet.co.id`) |
| Incoming | IMAP |
| Server IMAP | `sync.geonet.co.id` |
| Port IMAP | `993` |
| Enkripsi IMAP | SSL/TLS |
| Outgoing | SMTP |
| Server SMTP | `sync.geonet.co.id` |
| Port SMTP | `587` (STARTTLS) — **bukan** SSL/TLS di port 587 |
| Alternatif SMTP | Port `465` + **SSL/TLS** (jika 587 gagal) |
| Auth SMTP | Ya — sama dengan IMAP |
> `support@` pakai **authsource ldap** (grup AD `Mail-Users`, atribut `mail`). Inbound tetap via fetchmail POP3 dari hosting.
---
## Outlook (Windows)
1. **File → Add Account****Advanced setup** → **IMAP**
2. Incoming: `sync.geonet.co.id`, port **993**, SSL/TLS
3. Outgoing: `sync.geonet.co.id`, port **587**, STARTTLS
4. **More settings → Outgoing Server:** centang SMTP requires authentication, use same as incoming
5. Hapus akun lama jika masih `mail.geonet.co.id`
---
## Thunderbird
Manual config: IMAP 993 SSL, SMTP 587 STARTTLS, Normal password, username email penuh.
---
## Alur email masuk (transisi)
```
Internet → MX cPanel → inbox cPanel
↓ fetchmail POP3 (~2 menit)
inbox Mailcow → Outlook (IMAP)
```
User **tidak** perlu setting POP3 di PC — hanya IMAP ke Mailcow.
---
## Troubleshooting
| Gejala | Cek |
|--------|-----|
| Auth gagal | Password **AD** (bukan password Mailcow lama); pastikan user di grup `Mail-Users` |
| SMTP encryption error | Port **587** harus **STARTTLS** (bukan SSL/TLS). Port **465** pakai SSL/TLS |
| Cert error / encryption failed | Pastikan cert Let's Encrypt terpasang (`scripts/sync-ssl-from-nginx.sh`) |
| Email tidak masuk | MX masih cPanel — cek fetchmail: `tail /var/log/fetchmail-support.log` |
| Port blocked | `Test-NetConnection sync.geonet.co.id -Port 993` |
Detail server: [../server.md](../server.md), [../issues.md](../issues.md).
---
## Verifikasi koneksi (PowerShell)
```powershell
Test-NetConnection sync.geonet.co.id -Port 993
Test-NetConnection sync.geonet.co.id -Port 587
```
Keduanya harus `TcpTestSucceeded : True`.

28
Mailcow/docs/issues.md

@ -1,23 +1,19 @@ @@ -1,23 +1,19 @@
# Issues — Mailcow
---
## Open
## Open Issues
### ISS-001: _sogo_static_view auto-sync
### ISS-001: _sogo_static_view tidak auto-sync setelah edit password
### ISS-008: Cross-domain gisportal.id ↔ geonet.co.id (2026-07-06)
| Field | Value |
|-------|-------|
| Status | Open |
| Severity | Medium |
| Found | 2026-07-03 |
- `admin@gisportal.id` → user cPanel-only `@geonet.co.id`: cPanel reject (From vs SMTP auth)
- `rianjawa93@geonet.co.id` tidak ada di Mailcow
- Doc: [notes/gisportal-id-mail.md](./notes/gisportal-id-mail.md)
**Workaround:** UPDATE _sogo_static_view FROM mailbox.password; restart sogo container.
## Resolved
---
## Resolved Issues
### ISS-003: SOGo Unauthorized — authsource mailcow + sync view (2026-07-03)
### ISS-004: Gmail 550 — relay sc138:587 (2026-07-03)
### ISS-005: fetchmailrc parse — ssl after user, no sslcertck (2026-07-03)
- ISS-006 Internal mail cPanel user — transport Jalur B `rbsetiawan@` (2026-07-06)
- ISS-003 SOGo Unauthorized (2026-07-03)
- ISS-004 Gmail 550 relay (2026-07-03)
- ISS-005 fetchmail SSL (2026-07-03)
- ISS-007 Outlook SMTP cert (2026-07-06)

130
Mailcow/docs/notes/gisportal-id-mail.md

@ -0,0 +1,130 @@ @@ -0,0 +1,130 @@
# gisportal.id — Multi-Domain Mail (Mailcow murni)
> **Penting:** `gisportal.id` **tidak punya cPanel**. Domain ini langsung publik → `117.102.73.102` → port email di-forward ke Mailcow (`10.100.1.28`).
>
> Berbeda dengan `geonet.co.id` yang masih **hybrid** (MX/inbox banyak user di cPanel).
---
## Arsitektur dua domain di satu Mailcow
```
┌─────────────────────────────────────┐
│ Mailcow sync.geonet.co.id (.28) │
│ IP publik: 117.102.73.102 │
└─────────────────────────────────────┘
▲ ▲
MX mail.gisportal.id MX geonet.co.id (masih cPanel)
(langsung ke .102) + fetchmail / transport map
│ │
gisportal.id │ │ geonet.co.id
(NO cPanel) │ │ (hybrid cPanel)
│ │
admin@gisportal.id support@, rbsetiawan@, …
```
| Domain | Hosting email | Inbound | Outbound |
|--------|---------------|---------|----------|
| **gisportal.id** | **Mailcow saja** | MX → `.102` → Mailcow | Langsung dari Mailcow (tanpa relay cPanel) |
| **geonet.co.id** | Hybrid | MX masih cPanel + fetchmail / transport | Relay cPanel `:587` (transisi) |
Di Mailcow Admin, domain `gisportal.id` harus **`relayhost` = none** (bukan relay cPanel seperti `geonet.co.id`).
---
## DNS `gisportal.id` — wajib disesuaikan
SPF saat ini masih mencantumkan **IP cPanel lama** — itu salah untuk domain tanpa cPanel.
| Record | Target | Status |
|--------|--------|--------|
| MX | `10 mail.gisportal.id` | ✅ |
| A `mail.gisportal.id` | `117.102.73.102` | ✅ |
| A `gisportal.id` | `117.102.73.102` (web) | ✅ |
| **SPF** | `v=spf1 ip4:117.102.73.102 -all` | ✅ 2026-07-06 (TTL 1800, propagasi ~30 menit) |
| **DKIM** | TXT dari Mailcow Admin | ⏳ publish |
| PTR | `117.102.73.102``sync.geonet.co.id` | shared dengan geonet |
**SPF yang benar** (tanpa cPanel):
```txt
v=spf1 ip4:117.102.73.102 -all
```
**Hapus** entri lama seperti `ip4:103.229.73.23` dan `include:idse.idcloudhosting.com` dari SPF `gisportal.id` — itu sisa hosting lama, bukan Mailcow.
DKIM: Mailcow Admin → Configuration → Configuration & Details → **ARC/DKIM keys** → domain `gisportal.id` → publish TXT ke DNS.
---
## Perilaku `admin@gisportal.id`
### Yang sudah OK di server
| Arah | Contoh | Mekanisme |
|------|--------|-----------|
| Terima internal Mailcow | `support@``admin@gisportal.id` | LMTP lokal ✅ |
| Kirim ke internet | `admin@gisportal.id` → Gmail | SMTP langsung Mailcow ✅ |
| Kirim ke diri sendiri | SOGo self-test | ✅ |
### Masalah yang tersisa (bukan karena gisportal punya cPanel)
| Gejala | Penyebab sebenarnya |
|--------|---------------------|
| Kirim ke `rbsetiawan@geonet.co.id` gagal | **Penerima** di cPanel (`geonet.co.id` hybrid). Transport map pakai auth `support@geonet.co.id` → cPanel menolak From `@gisportal.id` |
| Kirim ke `rianjawa93@geonet.co.id` gagal | Mailbox tidak ada di Mailcow; tidak ada transport map |
| Kirim ke `support@` saja OK | `support@` punya mailbox aktif di Mailcow |
| Terima dari `rbsetiawan@` sempat ditolak | Email dari **cPanel geonet** lewat spam filter; Rspamd `SPOOFED_UNAUTH` — forwarding hosts sudah ditambahkan |
| Terima dari luar lambat/gagal | Tunggu propagasi SPF baru; pastikan DKIM sudah publish |
**Kirim ke user `@geonet.co.id` yang masih di cPanel** dari akun `@gisportal.id` memang bermasalah — ini konflik **cross-domain ke hybrid geonet**, bukan masalah hosting gisportal.
**Solusi kirim ke user cPanel-only `@geonet.co.id`:**
| Opsi | Keterangan |
|------|------------|
| A | Kirim dari `support@geonet.co.id` (bukan `@gisportal.id`) |
| B | Tambah transport map + kredensial SMTP yang domain-nya cocok (jarang praktis) |
| C | Migrasi penerima ke Mailcow (LDAP / mailbox) |
---
## Inbound ke `@gisportal.id` — alur yang benar
```
Pengirim (Gmail / rbsetiawan@cPanel / dll)
→ DNS MX gisportal.id = mail.gisportal.id
→ 117.102.73.102:25 (forward ke Mailcow)
→ Postfix Mailcow → inbox admin@gisportal.id
```
Tidak ada hop cPanel untuk domain `gisportal.id`.
Jika `rbsetiawan@` kirim dari **webmail cPanel geonet**, pastikan penerima `admin@gisportal.id` — MX lookup harus ke `mail.gisportal.id`, bukan lewat relay internal geonet saja.
---
## Checklist
- [ ] SPF `gisportal.id``v=spf1 ip4:117.102.73.102 -all` (hapus IP cPanel lama)
- [ ] DKIM publish dari Mailcow
- [ ] Verifikasi domain `gisportal.id` di Mailcow: **relayhost = none**
- [ ] Tes terima dari Gmail → `admin@gisportal.id`
- [ ] Tes terima dari `support@geonet.co.id` → refresh Inbox SOGo
- [ ] (Opsional) DMARC `_dmarc.gisportal.id`
---
## Script terkait
| Script | Fungsi |
|--------|--------|
| `scripts/fix-sogo-domain.sh` | Sync password SOGo setelah buat mailbox |
| `scripts/setup-gisportal-inbound-whitelist.sh` | Whitelist NAT/cPanel forwarder untuk inbound |
---
## Referensi
- [hybrid-migration-cpanel-mailcow.md](./hybrid-migration-cpanel-mailcow.md) — hanya untuk **geonet.co.id**
- [ldap-ad-mailcow.md](./ldap-ad-mailcow.md) — AD domain `gisportal.id` vs mailbox `@geonet.co.id` / `@gisportal.id`

128
Mailcow/docs/notes/hybrid-migration-cpanel-mailcow.md

@ -0,0 +1,128 @@ @@ -0,0 +1,128 @@
# Hybrid Migration — User @geonet.co.id (cPanel vs Mailcow)
> **Kapan baca:** User masih baca email di cPanel, tapi sudah masuk grup `Mail-Users` / punya mailbox Mailcow.
---
## Masalah
```
support@ (Mailcow) --kirim--> rbsetiawan@geonet.co.id
|
Postfix Mailcow: LOCAL delivery
(karena mailbox ada di Mailcow)
|
inbox Mailcow saja
X
inbox cPanel (MX eksternal)
```
| Arah email | rbsetiawan baca di | Masuk ke |
|------------|-------------------|----------|
| Internet → MX cPanel | cPanel | ✅ cPanel |
| support@ → rbsetiawan@ (internal Mailcow) | cPanel | ❌ hanya Mailcow |
**Satu alamat email = satu inbox canonical.** Tidak bisa mailbox aktif di Mailcow **dan** inbox utama di cPanel untuk routing internal.
---
## Dua jalur per user
### Jalur A — Migrasi penuh ke Mailcow (disarankan jangka panjang)
Sesuai [`email-sync-migration-concept.md`](./email-sync-migration-concept.md).
| Step | Aksi |
|------|------|
| 1 | Tetap di grup `Mail-Users` |
| 2 | Mailbox Mailcow aktif (LDAP) |
| 3 | Tambah **fetchmail POP3** dari cPanel → Mailcow (seperti `support@`) |
| 4 | User ganti Outlook → IMAP `sync.geonet.co.id` + password AD |
| 5 | Inbox canonical = **Mailcow saja** |
Email internal dari `support@` sudah masuk Mailcow — user tinggal baca di sana.
### Jalur B — Tetap di cPanel (belum migrasi)
| Step | Aksi |
|------|------|
| 1 | **Keluarkan** user dari grup AD `Mail-Users` |
| 2 | Nonaktifkan mailbox di Mailcow Admin (**Inactive**) |
| 3 | LDAP sync: `docker exec mailcowdockerized-php-fpm-mailcow-1 php /crons/ldap-sync.php` |
| 4 | **Transport map** → relay ke SMTP cPanel (wajib) |
| 5 | Aktifkan alias (jika ada) agar SOGo terima penerima |
| 6 | User tetap baca email di cPanel |
Tanpa transport map: kirim internal dari Mailcow ke user tanpa mailbox → error `User unknown`.
#### Transport map (Mailcow Admin UI)
**Configuration → Routing → Transport maps → Add**
| Field | Contoh `rbsetiawan@` |
|-------|----------------------|
| Destination | `rbsetiawan@geonet.co.id` |
| Nexthop | `[sc138.idcloudhosting.cloud]:587` |
| Username | `support@geonet.co.id` (sama relay outbound) |
| Password | password SMTP cPanel relay |
| Active | ✅ |
Atau jalankan script: `scripts/setup-transport-cpanel-user.sh rbsetiawan@geonet.co.id`
**Catatan:** Port **25** ke cPanel ditolak RBL (IP publik Mailcow). Pakai **587 + auth**.
Verifikasi Postfix: `postmap -q rbsetiawan@geonet.co.id mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf` → harus return `smtp_via_transport_maps:[sc138...]:587`
---
## Kebijakan tim (rekomendasi)
| Fase user | Grup `Mail-Users` | Mailbox Mailcow | Baca email via |
|-----------|-------------------|-----------------|----------------|
| **Belum migrasi** | ❌ Jangan tambah dulu | ❌ | cPanel |
| **Migrasi** | ✅ | ✅ | `sync.geonet.co.id` IMAP |
| **support@** | ✅ | ✅ + fetchmail POP3 | Mailcow |
**Jangan** masukkan semua user ke `Mail-Users` sekaligus jika masih pakai cPanel harian.
---
## Untuk kasus rbsetiawan@ saat ini (Jalur B — diterapkan 2026-07-06)
| Item | Status |
|------|--------|
| Keluar grup `Mail-Users` | ✅ |
| Mailbox Mailcow Inactive | ✅ (`active=0`) |
| LDAP sync | ✅ |
| Transport map → cPanel :587 | ✅ |
| Alias aktif (SOGo RCPT) | ✅ |
| Tes kirim support@ → rbsetiawan@ | ✅ verified — inbox cPanel |
Email lama yang sudah masuk inbox Mailcow **tidak otomatis pindah** ke cPanel.
Untuk migrasi penuh (Jalur A): fetchmail POP3 + Outlook IMAP Mailcow — lihat [`email-sync-migration-concept.md`](./email-sync-migration-concept.md).
---
## fetchmail multi-user
Contoh tambahan di `/opt/fetchmail/fetchmailrc` (satu entry per user migrasi):
```
poll mail.geonet.co.id with proto POP3 port 995
user "rbsetiawan@geonet.co.id" there with password "..." is "rbsetiawan@geonet.co.id" here
ssl
no sslcertck
smtphost 127.0.0.1
smtpname sync.geonet.co.id
fetchall
nokeep
```
---
## Referensi
- [email-sync-migration-concept.md](./email-sync-migration-concept.md)
- [ldap-ad-mailcow.md](./ldap-ad-mailcow.md)
- [issues.md](../issues.md) ISS-006

24
Mailcow/docs/notes/ldap-ad-mailcow.md

@ -1,13 +1,26 @@ @@ -1,13 +1,26 @@
# LDAP — Mailcow ke Active Directory
> **Terakhir Diperbarui:** 2026-07-03
> **Terakhir Diperbarui:** 2026-07-06
> **AD:** `10.100.1.40` · domain `gisportal.id`
> **Status:** Grup AD siap ✅ · LDAP Identity Provider aktif ✅ (2026-07-03)
> **Status:** **26 mailbox** `@geonet.co.id` via LDAP ✅ (2026-07-06)
> ⚠ Bind password **tidak** didokumentasikan di repo — reuse `geonet-console` `.env`
---
## Status AD — Mail-Users (2026-07-03)
## Status AD — Mail-Users (2026-07-06)
| Item | Nilai | Status |
|------|-------|--------|
| Grup LDAP | `Mail-Users` | ✅ |
| DN grup | `CN=Mail-Users,CN=Users,DC=gisportal,DC=id` | ✅ |
| Mailbox di Mailcow | **26** user `@geonet.co.id` | ✅ LDAP sync |
| Kirim internal | `support@``rbsetiawan@` dll. | ✅ verified |
**Onboard user baru:** tambah ke grup `Mail-Users` + atribut `mail` = `user@geonet.co.id` → jalankan `php /crons/ldap-sync.php` atau tunggu periodic sync (15 menit).
---
## Status AD — Mail-Users (awal 2026-07-03)
| Item | Nilai | Status |
|------|-------|--------|
@ -216,7 +229,7 @@ Setelah LDAP aktif: @@ -216,7 +229,7 @@ Setelah LDAP aktif:
| **Outlook** | `user@geonet.co.id` + **password AD** | IMAP/SMTP `sync.geonet.co.id` |
| **Zammad channel** | Service account `support@geonet.co.id` | Bisa tetap password mailbox khusus *(bukan LDAP user login)* |
> Mailbox `support@` untuk Zammad/fetchmail bisa tetap **authsource mailcow (SQL)** dengan app password, sementara user biasa pakai LDAP — menghindari konflik service account.
> **Keputusan 2026-07-06:** `support@` memakai **authsource ldap** (satu mailbox). fetchmail POP3 tetap; user IMAP password AD. Lihat [ADR-0001-support-ldap-auth.md](../adr/ADR-0001-support-ldap-auth.md).
---
@ -273,6 +286,9 @@ Pertahankan akun `admin` SQL terpisah — **jangan** pakai LDAP untuk admin pane @@ -273,6 +286,9 @@ Pertahankan akun `admin` SQL terpisah — **jangan** pakai LDAP untuk admin pane
| Email salah domain | `mail` di AD masih `@gisportal.id` | Update AD → `@geonet.co.id` |
| Connection test gagal | Firewall / bind password | `nc` ke `.40:389`, cek bind DN |
| Semua user AD dapat mailbox | Filter terlalu luas | Perketat `memberOf=CN=Mail-Users,...` |
| **SOGo Unauthorized** (domain baru) | `_sogo_static_view` password tidak sinkron; domain belum di SOGo plist | Reset password di Admin UI → jalankan `scripts/fix-sogo-domain.sh` |
| **SOGo Unauthorized** (`authsource=mailcow`) | Password SOGo ≠ password mailbox; atau pakai password **AD** salah | Pakai password **Mailcow mailbox** (bukan `Administrator@gisportal.id` AD) |
| `/user` OK, `/SOGo/so/` 403 tanpa login | Session terpisah | Login form SOGo, atau buka Webmail dari menu `/user` |
---

68
Mailcow/docs/project-status.md

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
# Project Status — Mailcow
> **Terakhir Diperbarui:** 2026-07-03
> **Terakhir Diperbarui:** 2026-07-06
> **Versi:** 1.2.5 — [version.md](./version.md)
---
@ -10,65 +11,46 @@ @@ -10,65 +11,46 @@
|-----------|---------|---------|
| **VM IP** | `10.100.1.28` | 2026-07-03 |
| **vCPU / RAM** | 16 core / 32 GiB | 2026-07-03 |
| **Disk 1** | 512 GiB SSD — OS + DB | 2026-07-03 |
| **Disk 2** | 512 GiB SSD — vmail live | 2026-07-03 |
| **Prioritas storage** | Uptime layanan mail — local SSD | 2026-07-03 |
| **Dual domain** | `geonet.co.id` hybrid + `gisportal.id` murni | 2026-07-06 |
| **gisportal.id** | Tanpa cPanel; MX langsung ke Mailcow | 2026-07-06 |
| **Backup** | QNAP `10.100.1.10` harian | 2026-07-03 |
Mail tetap jalan meski NAS maintenance atau gangguan LAN ke QNAP.
---
## Ringkasan
| Item | Status |
|------|--------|
| Dokumentasi repo | ✅ |
| Konsep POP3 drain → Mailcow | ✅ `docs/notes/email-sync-migration-concept.md` |
| Konsep LDAP AD | ✅ `docs/notes/ldap-ad-mailcow.md` |
| Grup AD `Mail-Users` | ✅ 2026-07-03 |
| Member `support` + `mail` | ✅ `support@geonet.co.id` |
| **Storage live** | Local SSD VM + backup QNAP | ✅ Keputusan 2026-07-03 |
| **VM Mailcow** | `10.100.1.28` — 16 vCPU, 32 GB, 2×512 GB SSD | ✅ |
| SSH key login | `ssh mailcow` | ✅ 2026-07-03 |
| OS prep (hostname, disk, docker) | ✅ 2026-07-03 |
| Desktop GNOME (Proxmox console) | ✅ ubuntu-desktop-minimal |
| Install Mailcow stack | ✅ 2026-07-03 — `/opt/stacks/mailcow` |
| Domain `geonet.co.id` | ✅ aktif, DKIM generated |
| Mailbox `support@geonet.co.id` | ✅ auth mailcow (service account) |
| SOGo login + kirim/terima | ✅ verified user 2026-07-03 |
| Outbound relay cPanel | ✅ `sc138.idcloudhosting.cloud:587` |
| Nginx proxy `sync.geonet.co.id` | ✅ `.24` + Let's Encrypt |
| LDAP Identity Provider | ✅ 2026-07-03 (user AD lain) |
| fetchmail POP3 drain | ✅ timer 2 menit — `scripts/install-fetchmail-support.sh` |
| Domain `geonet.co.id` | ✅ hybrid cPanel (fetchmail + relay + transport maps) |
| Domain `gisportal.id` | ✅ Mailcow murni; mailbox `admin@gisportal.id` |
| LDAP 26 mailbox `@geonet.co.id` | ✅ |
| Jalur B `rbsetiawan@` transport | ✅ verified |
| SOGo `admin@gisportal.id` | ✅ |
| Outbound Gmail dari gisportal | ✅ |
| SPF `gisportal.id` (Master Web) | ✅ propagasi OK |
| DKIM `gisportal.id` | ⏳ |
| SPF/DKIM `geonet.co.id` | ⏳ |
| Zammad IMAP → Mailcow | ❌ |
| DKIM/SPF DNS publish | ❌ |
| MX cutover geonet | ❌ |
---
## Active Directory — Mail-Users
## Hybrid geonet.co.id
| Item | Nilai |
|------|-------|
| Grup | `Mail-Users` |
| DN (asumsi default) | `CN=Mail-Users,CN=Users,DC=gisportal,DC=id` |
| Member pertama | `support` |
| Email (`mail`) | `support@geonet.co.id` |
| Login AD | `support` (sAMAccountName) |
| Jalur | Deskripsi | Contoh |
|-------|-----------|--------|
| A | Full migrate: Mail-Users + mailbox aktif | `support@` |
| B | cPanel-only: inactive + transport map | `rbsetiawan@` |
> Verifikasi DN grup jika LDAP filter gagal: PowerShell di AD → `(Get-ADGroup 'Mail-Users').DistinguishedName`
Script: `setup-transport-cpanel-user.sh`
---
## Berikutnya
1. Provision VM + Mailcow + LDAP | ✅
2. Domain `geonet.co.id` + mailbox `support@` | ✅
3. DNS email (DKIM/SPF) untuk `geonet.co.id` | ⏳
4. fetchmail POP3 drain dari cPanel | ✅
5. Outbound relay cPanel (transisi) | ✅
6. SOGo `support@` operational | ✅
7. DNS DKIM/SPF + PTR | ⏳
8. Zammad IMAP/SMTP → Mailcow | ⏳
1. SPF `gisportal.id``v=spf1 ip4:117.102.73.102 -all` (Master Web)
2. DKIM `gisportal.id` + `geonet.co.id` publish
3. Zammad IMAP/SMTP channel
4. MX cutover `geonet.co.id`
Lihat `docs/todo.md`, `docs/ai-session-summary.md`.
Lihat `docs/todo.md`, `docs/ai-session-summary.md`, `docs/notes/gisportal-id-mail.md`.

101
Mailcow/docs/system-requirements.md

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
# System Requirements — Mailcow
**Owner:** runtime minimum agar Mailcow + fetchmail berjalan.
Inventori host: [server.md](./server.md). Env: [.env.example](../.env.example).
---
## Ringkasan
| Field | Value |
|-------|-------|
| Project | `Mailcow/` |
| Tipe | infra (mail server) |
| Last updated | 2026-07-06 |
---
## Platform & OS
| Komponen | Minimum | Dipakai saat ini |
|----------|---------|------------------|
| OS server | Ubuntu 22.04 LTS | Ubuntu 22.04.5 LTS |
| Arsitektur | x64 | x64 |
| `vm.max_map_count` | 262144 | 262144 |
---
## Runtime & Toolchain
| Tool | Minimum | Dipakai | Cek |
|------|---------|---------|-----|
| Docker | 24+ | 29.x | `docker --version` |
| Docker Compose | v2 | v5 | `docker compose version` |
| fetchmail | 6.x | systemd package | `fetchmail -V` |
| systemd | — | timer 2 menit | `systemctl status fetchmail-support.timer` |
---
## Resource minimum (VM Mailcow)
| Lingkungan | CPU | RAM | Disk |
|------------|-----|-----|------|
| prod (30+ user) | 8 vCPU | 16 GiB | 2×256 GiB SSD |
| prod (saat ini) | **16 vCPU** | **32 GiB** | 2×512 GiB SSD |
Disk 2: `/data/mailcow/vmail` (Dovecot mail storage live).
---
## Dependency eksternal
| Dependency | Host | Dipakai untuk |
|------------|------|---------------|
| nginx reverse proxy | `10.100.1.24` | HTTPS UI `sync.geonet.co.id` |
| Active Directory LDAP | `10.100.1.40:389` | Identity Provider (bukan `support@`) |
| cPanel POP3 | `mail.geonet.co.id:995` | fetchmail drain (transisi) |
| cPanel SMTP relay | `sc138.idcloudhosting.cloud:587` | Outbound transisi |
| QNAP NAS | `10.100.1.10` | Backup harian |
| Dockge primary | `10.100.1.24` | Manajemen stack |
---
## Variabel URL & konfigurasi (no hardcode)
| Variabel | Wajib | Contoh | Dipakai oleh |
|----------|-------|--------|--------------|
| `MAILCOW_HOSTNAME` | ya | `sync.geonet.co.id` | Mailcow generate_config |
| `MAILCOW_VM_IP` | ya | `10.100.1.28` | docs, SSH |
| `MAILCOW_SUPPORT_EMAIL` | ya | `support@geonet.co.id` | fetchmail, Zammad |
| `MAILCOW_SUPPORT_PASSWORD` | ya | *(secret)* | fetchmail, SOGo, SMTP |
| `LDAP_HOST` | opsional | `10.100.1.40` | Identity Provider |
| `LEGACY_SMTP_HOST` | transisi | `sc138.idcloudhosting.cloud` | relay outbound |
Detail: [environment.md](./environment.md), [.env.example](../.env.example).
---
## Port & protokol
| Service | Port | Protokol |
|---------|------|----------|
| SMTP inbound | 25 | TCP |
| SMTPS | 465 | TCP/TLS |
| Submission | 587 | TCP/STARTTLS |
| IMAPS | 993 | TCP/TLS |
| POP3S | 995 | TCP/TLS |
| HTTPS (UI) | 443 | HTTPS |
| Dockge agent | 5001 | HTTP (LAN only) |
---
## Client IMAP (PC user)
| Setting | Nilai |
|---------|-------|
| Server | `sync.geonet.co.id` |
| Port | 993 SSL/TLS |
| Username | `support@geonet.co.id` |
| Password | Password Mailcow (bukan AD) |
Panduan lengkap: [guide/outlook-imap-setup.md](./guide/outlook-imap-setup.md).

44
Mailcow/docs/todo.md

@ -1,47 +1,39 @@ @@ -1,47 +1,39 @@
# TODO — Mailcow
> Last updated: 2026-07-03
> Last updated: 2026-07-06
---
## High Priority
- [ ] Publish DKIM TXT dkim._domainkey.geonet.co.id (key dari Mailcow API)
- [ ] Update SPF geonet.co.id — include IP 117.102.73.102 atau relay sementara
- [ ] Zammad channel email → IMAP/SMTP Mailcow 10.100.1.28 (ganti cPanel)
- [ ] **DKIM `gisportal.id`** publish TXT dari Mailcow Admin → Master Web
- [ ] Verifikasi terima/kirim `admin@gisportal.id` (Gmail ↔ SOGo) setelah DKIM
- [ ] Publish DKIM + SPF `geonet.co.id`
- [ ] Zammad channel email → IMAP/SMTP Mailcow `10.100.1.28`
---
## Medium Priority
- [ ] PTR/rDNS 117.102.73.102 → sync.geonet.co.id
- [ ] Buat scripts/install-relay-cpanel.sh (mirror fetchmail installer)
- [ ] Automasi sync _sogo_static_view setelah password mailbox mailcow berubah
- [ ] Monitoring fetchmail (Uptime Kuma atau alert log)
- [ ] PTR/rDNS `117.102.73.102``sync.geonet.co.id`
- [ ] Script `install-relay-cpanel.sh`
- [ ] Auto-sync `_sogo_static_view` setelah ganti password
- [ ] Hapus relayhost id=2 typo di Mailcow Admin (jika masih ada)
---
## Low Priority
- [ ] fetchmail entry untuk mailbox tambahan (info@, dll.)
- [ ] LDAP user lain di Mail-Users — provisioning + Outlook guide
- [ ] Domain sekunder gegeo.my.id
- [ ] MX cutover ke sync.geonet.co.id (setelah 2–4 minggu stabil)
- [ ] Matikan fetchmail + relay cPanel pasca cutover
- [ ] MX cutover `geonet.co.id``sync.geonet.co.id`
- [ ] Matikan fetchmail + relay cPanel setelah cutover
- [ ] Transport map `rianjawa93@` jika tetap di cPanel
---
## Selesai (Sesi 2026-07-03)
## Selesai (Sesi Ini)
- [x] fetchmail POP3 drain support@ + timer 2 menit
- [x] Relay outbound cPanel :587 untuk geonet.co.id
- [x] SOGo login + kirim/terima email verified
- [x] scripts/install-fetchmail-support.sh
- [x] Dokumentasi handover sesi
---
## Technical Debt
- [ ] support@ service account vs LDAP — dokumentasikan keputusan
- [ ] context-index diagram IP — align ke .28
- [x] Jalur B `rbsetiawan@`: transport map + verified `support@` → cPanel
- [x] Domain `gisportal.id` + mailbox `admin@gisportal.id` + SOGo
- [x] Forwarding hosts whitelist inbound gisportal
- [x] SPF `gisportal.id`: `v=spf1 ip4:117.102.73.102 -all` (Master Web, propagasi OK)
- [x] Scripts + docs dual-domain (`gisportal-id-mail.md`, hybrid migration)

89
Mailcow/docs/version.md

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
# Version History
**Owner:** riwayat versi deploy/config Mailcow — append per perubahan material.
Sinkron dengan [changelog.md](./changelog.md).
**Wajib sebelum deploy:** append entri baru di sini (bullet poin jelas).
---
## [1.2.5] - 2026-07-06
- SPF `gisportal.id` dipublish di Master Web: `v=spf1 ip4:117.102.73.102 -all`
---
## [1.2.4] - 2026-07-06
- Dokumentasi `gisportal.id`: domain **Mailcow murni** (tanpa cPanel), langsung `.102` → Mailcow
- SPF `gisportal.id` harus diganti: `v=spf1 ip4:117.102.73.102 -all` (hapus IP cPanel lama)
- Whitelist forwarding hosts untuk inbound dari pengirim cPanel geonet
- Cross-domain `@gisportal.id` → user cPanel-only `@geonet.co.id` tetap terbatas (bukan masalah cPanel gisportal)
---
## [1.2.3] - 2026-07-06
- **Jalur B hybrid** `rbsetiawan@geonet.co.id`: keluar `Mail-Users`, mailbox Inactive, transport map ke cPanel
- Transport: `rbsetiawan@geonet.co.id``[sc138.idcloudhosting.cloud]:587` (auth `support@`)
- Alias `rbsetiawan@` diaktifkan agar SOGo terima penerima
- Script: `scripts/setup-transport-cpanel-user.sh`
- Port 25 ke cPanel ditolak RBL; wajib relay :587
- **Verified:** kirim `support@` (Mailcow/LDAP) → `rbsetiawan@` (cPanel) OK
---
## [1.2.2] - 2026-07-06
- Semua user `@geonet.co.id` ditambahkan ke grup AD **Mail-Users**
- LDAP sync: **26 mailbox** aktif (`authsource=ldap`)
- Kirim internal `support@``rbsetiawan@` verified (SOGo/webmail)
- Kirim eksternal → Gmail verified
---
## [1.2.1] - 2026-07-06
- Fix Outlook SMTP: pasang cert **Let's Encrypt** ke Postfix/Dovecot (salin dari nginx `.24`)
- Script: `scripts/sync-ssl-from-nginx.sh`
- Penyebab error: port 587 pakai cert self-signed + Outlook port 587 harus STARTTLS
---
## [1.2.0] - 2026-07-06
- `support@geonet.co.id` authsource **mailcow → ldap** (satu mailbox, inbox tidak dipindah)
- User IMAP/SMTP: password **Active Directory** (`Mail-Users`)
- fetchmail POP3 drain **tetap aktif** — inbound SSOT hosting → inbox Mailcow sama
- Script: `scripts/switch-support-to-ldap.sh`
- ADR: `docs/adr/ADR-0001-support-ldap-auth.md`
- Inbox verified: 11 pesan setelah switch
---
## [1.1.0] - 2026-07-06
- Bootstrap dokumentasi AI-Agent-Standards (AGENTS.md, version.md, system-requirements.md, guide/)
- Panduan IMAP/SMTP Outlook user → `docs/guide/outlook-imap-setup.md`
- Verifikasi fetchmail POP3 drain aktif (timer 2 menit, inbox Mailcow 9 pesan)
- Perbaikan encoding `changelog.md` / `todo.md` (UTF-8)
---
## [1.0.0] - 2026-07-03
- Mailcow stack live di VM `10.100.1.28` (`/opt/stacks/mailcow`)
- Domain `geonet.co.id` + mailbox `support@geonet.co.id` (authsource mailcow)
- fetchmail POP3 drain cPanel → Mailcow (`scripts/install-fetchmail-support.sh`, timer 2 menit)
- Outbound relay cPanel `sc138.idcloudhosting.cloud:587` (transisi, Gmail tidak reject)
- SOGo login + kirim/terima verified
- LDAP Identity Provider untuk user AD (`Mail-Users`)
- Nginx proxy `sync.geonet.co.id` di `10.100.1.24` + Let's Encrypt
- Dockge agent port `5001` di VM Mailcow
---
## [0.1.0] - 2026-07-01
- Inisialisasi dokumentasi repo Mailcow
- Konsep POP3 drain + LDAP AD (`docs/notes/`)

37
Mailcow/scripts/fix-sogo-domain.sh

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
#!/bin/bash
# Fix SOGo for mailcow-auth mailboxes (authsource=mailcow), especially new domains.
# Run after: new domain/mailbox, password change in Admin UI, or SOGo Unauthorized.
# Usage: fix-sogo-domain.sh [user@domain.tld]
set -euo pipefail
STACK='/opt/stacks/mailcow'
USER="${1:-admin@gisportal.id}"
echo "== Sync c_password from mailbox (authsource=mailcow) =="
docker exec mailcowdockerized-mysql-mailcow-1 bash -lc '
mysql mailcow -uroot -p"$MYSQL_ROOT_PASSWORD" -e "
UPDATE _sogo_static_view s
INNER JOIN mailbox m ON m.username = s.c_name
SET s.c_password = m.password
WHERE m.authsource = \"mailcow\" AND m.active IN (\"1\",\"2\");
"
'
echo "== Verify: $USER =="
docker exec mailcowdockerized-mysql-mailcow-1 bash -lc "
mysql mailcow -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -N -e \"
SELECT 'mailbox', LEFT(password,55) FROM mailbox WHERE username='${USER}';
SELECT 'sogo', LEFT(c_password,55) FROM _sogo_static_view WHERE c_name='${USER}';
\"
"
echo "== Restart SOGo =="
cd "$STACK"
docker compose restart sogo-mailcow
sleep 8
echo "== SOGo domains =="
docker exec mailcowdockerized-sogo-mailcow-1 grep -E '<key>(geonet|gisportal|gegeo)' /var/lib/sogo/GNUstep/Defaults/sogod.plist || \
docker exec mailcowdockerized-sogo-mailcow-1 grep '<key>.*\.id</key>' /var/lib/sogo/GNUstep/Defaults/sogod.plist
echo ""
echo "DONE. Login SOGo with Mailcow mailbox password (NOT AD password for authsource=mailcow)."

19
Mailcow/scripts/setup-gisportal-inbound-whitelist.sh

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
#!/bin/bash
# Fix inbound mail to @gisportal.id from cPanel/NAT path (rspamd SPOOFED_UNAUTH)
set -euo pipefail
echo "== Add forwarding hosts (trusted inbound relays) =="
docker exec mailcowdockerized-mysql-mailcow-1 bash -lc '
mysql mailcow -uroot -p"$MYSQL_ROOT_PASSWORD" -e "
INSERT IGNORE INTO forwarding_hosts (host, source, filter_spam) VALUES
(\"10.100.1.1\", \"NAT gateway inbound\", 1),
(\"103.229.73.23\", \"cPanel sc138\", 1),
(\"sc138.idcloudhosting.cloud\", \"cPanel SMTP hostname\", 1),
(\"spfilter-2.idcloudhosting.com\", \"cPanel spam filter\", 1);
SELECT host, source FROM forwarding_hosts;
"
'
docker exec mailcowdockerized-postfix-mailcow-1 postfix reload
docker exec mailcowdockerized-rspamd-mailcow-1 rspamadm reload 2>/dev/null || true
echo "DONE forwarding hosts"

79
Mailcow/scripts/setup-transport-cpanel-user.sh

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
#!/bin/bash
# Path B hybrid: relay internal mail for a cPanel-only user via transport map.
# Prerequisite: user removed from AD Mail-Users, mailbox inactive in Mailcow Admin.
# Usage: setup-transport-cpanel-user.sh user@geonet.co.id
set -euo pipefail
EMAIL="${1:-}"
NEXTHOP="${TRANSPORT_NEXTHOP:-[sc138.idcloudhosting.cloud]:587}"
RELAYHOST_ID="${TRANSPORT_RELAYHOST_ID:-1}"
STACK='/opt/stacks/mailcow'
MYSQL_C='mailcowdockerized-mysql-mailcow-1'
POSTFIX_C='mailcowdockerized-postfix-mailcow-1'
PHP_C='mailcowdockerized-php-fpm-mailcow-1'
if [ -z "$EMAIL" ]; then
echo "Usage: $0 user@geonet.co.id"
exit 1
fi
mysql_q() {
docker exec "$MYSQL_C" bash -lc "mysql mailcow -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -N -e \"$1\""
}
mysql_exec() {
docker exec "$MYSQL_C" bash -lc "mysql mailcow -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -e \"$1\""
}
echo "== LDAP sync =="
docker exec "$PHP_C" php /crons/ldap-sync.php || true
echo "== Mailbox check (expect active=0) =="
MB=$(mysql_q "SELECT username, active, authsource FROM mailbox WHERE username='${EMAIL}';")
echo "$MB"
if echo "$MB" | grep -qP '\t1\t'; then
echo "WARN: mailbox still active — deactivate in Mailcow Admin first (Path B)"
fi
echo "== Transport map =="
CNT=$(mysql_q "SELECT COUNT(*) FROM transports WHERE destination='${EMAIL}';")
if [ "${CNT}" = "0" ]; then
mysql_exec "
INSERT INTO transports (destination, nexthop, username, password, is_mx_based, active)
SELECT '${EMAIL}', '${NEXTHOP}', r.username, r.password, 0, 1
FROM relayhosts r
WHERE r.id=${RELAYHOST_ID} AND r.active=1
LIMIT 1;
"
echo "Inserted transport for ${EMAIL} -> ${NEXTHOP}"
else
mysql_exec "
UPDATE transports t
JOIN relayhosts r ON r.id=${RELAYHOST_ID} AND r.active=1
SET t.nexthop='${NEXTHOP}', t.username=r.username, t.password=r.password, t.active=1
WHERE t.destination='${EMAIL}';
"
echo "Updated existing transport for ${EMAIL}"
fi
mysql_exec "SELECT id, destination, nexthop, username, is_mx_based, active FROM transports WHERE destination='${EMAIL}';"
echo "== Alias (SOGo RCPT acceptance) =="
ALIAS=$(mysql_q "SELECT COUNT(*) FROM alias WHERE address='${EMAIL}';")
if [ "${ALIAS}" != "0" ]; then
mysql_exec "UPDATE alias SET active=1 WHERE address='${EMAIL}';"
mysql_exec "SELECT address, goto, active FROM alias WHERE address='${EMAIL}';"
else
echo "No alias row for ${EMAIL} (OK if sending only via postfix, not SOGo)"
fi
echo "== Postfix lookup =="
docker exec "$POSTFIX_C" postmap -q "${EMAIL}" mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf || true
echo "== Reload postfix =="
docker exec "$POSTFIX_C" postfix reload
echo ""
echo "DONE. Test: send from support@ to ${EMAIL}, verify inbox on cPanel."
echo "Admin UI: Configuration -> Routing -> Transport maps"

49
Mailcow/scripts/switch-support-to-ldap.sh

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
#!/bin/bash
# Switch support@geonet.co.id from authsource mailcow → ldap
# Inbox/fetchmail unchanged — only IMAP/SMTP login uses AD password
set -euo pipefail
MAILBOX='support@geonet.co.id'
STACK='/opt/stacks/mailcow'
MYSQL_C='mailcowdockerized-mysql-mailcow-1'
DOVECOT_C='mailcowdockerized-dovecot-mailcow-1'
SOGO_C='mailcowdockerized-sogo-mailcow-1'
PHP_C='mailcowdockerized-php-fpm-mailcow-1'
mysql_q() {
docker exec "$MYSQL_C" bash -lc "mysql mailcow -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -N -e \"$1\""
}
echo "== Pre-check =="
BEFORE=$(mysql_q "SELECT username, active, authsource FROM mailbox WHERE username='${MAILBOX}';")
echo "Mailbox: $BEFORE"
if ! echo "$BEFORE" | grep -q "$MAILBOX"; then
echo "ERROR: mailbox ${MAILBOX} not found"
exit 1
fi
echo "== LDAP sync (import/provision) =="
docker exec "$PHP_C" php /crons/ldap-sync.php || true
echo "== Switch authsource to ldap =="
mysql_q "UPDATE mailbox SET authsource='ldap' WHERE username='${MAILBOX}';"
AFTER=$(mysql_q "SELECT username, active, authsource FROM mailbox WHERE username='${MAILBOX}';")
echo "Mailbox after: $AFTER"
echo "== SOGo static view (LDAP user) =="
# LDAP users: clear local password hash in SOGo view; SOGo authenticates via LDAP
docker exec "$MYSQL_C" bash -lc "mysql mailcow -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -e \"
UPDATE _sogo_static_view SET c_password = NULL WHERE c_name = '${MAILBOX}';
\"" || echo "WARN: _sogo_static_view update skipped (table/row may differ)"
echo "== Restart mail services =="
cd "$STACK"
docker compose restart dovecot-mailcow sogo-mailcow
echo "== fetchmail timer =="
systemctl is-active fetchmail-support.timer
docker exec "$DOVECOT_C" doveadm mailbox status -u "$MAILBOX" messages INBOX 2>/dev/null || true
echo ""
echo "DONE. Test IMAP with AD password for ${MAILBOX}"
echo "fetchmail POP3 drain unchanged (cPanel -> same inbox)"

18
Mailcow/scripts/sync-ssl-from-nginx.sh

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
#!/bin/bash
# Sync Let's Encrypt cert from nginx (.24) to Mailcow SMTP/IMAP (.28)
# Run from workstation with SSH to both hosts, or schedule after certbot renew on .24
set -euo pipefail
NGINX_HOST="${NGINX_HOST:-10.100.1.24}"
MAILCOW_HOST="${MAILCOW_HOST:-10.100.1.28}"
DOMAIN="${DOMAIN:-sync.geonet.co.id}"
STACK="/opt/stacks/mailcow"
CERT_SRC="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"
KEY_SRC="/etc/letsencrypt/live/${DOMAIN}/privkey.pem"
echo "Copy LE cert ${DOMAIN} -> Mailcow ${MAILCOW_HOST}"
ssh root@"${NGINX_HOST}" "cat ${CERT_SRC}" | ssh root@"${MAILCOW_HOST}" "cat > ${STACK}/data/assets/ssl/cert.pem"
ssh root@"${NGINX_HOST}" "cat ${KEY_SRC}" | ssh root@"${MAILCOW_HOST}" "cat > ${STACK}/data/assets/ssl/key.pem"
ssh root@"${MAILCOW_HOST}" "chmod 600 ${STACK}/data/assets/ssl/key.pem && openssl x509 -in ${STACK}/data/assets/ssl/cert.pem -noout -issuer"
ssh root@"${MAILCOW_HOST}" "cd ${STACK} && docker compose restart postfix-mailcow dovecot-mailcow nginx-mailcow"
echo "DONE"

20
Mikrotik/.env.example

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
# MikroTik — template (AMAN di-commit)
# Credential SSH: key ~/.ssh/id_rsa_mikrotik — bukan di file ini
MIKROTIK_HOST=10.100.1.1
MIKROTIK_HOST_OFFICE=192.168.0.1
MIKROTIK_SSH_PORT=255
MIKROTIK_SSH_USER=admin
MIKROTIK_SSH_MAC=hmac-sha1
# WAN publik (blok ISP)
WAN_PUBLIC_BLOCK=117.102.73.96/29
WAN_GATEWAY_ISP1=117.102.73.97
WAN_IP_PRIMARY=117.102.73.102
# Segment internal
LAN_SERVER=10.100.1.0/24
LAN_OFFICE=192.168.0.0/24
LAN_VPN=100.100.1.0/24
# Dokumentasi: docs/server.md, docs/system-requirements.md

7
Mikrotik/.gitignore vendored

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
# Local secrets — never commit
.env
.env.*
!.env.example
# Optional: full export with show-sensitive (if ever used)
backups/*-sensitive.rsc

36
Mikrotik/AGENTS.md

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
# AGENTS.md — Cold Start
> Project: `Mikrotik/`
> Sesi: [docs/ai-session-summary.md](./docs/ai-session-summary.md)
> Peta: [docs/context-index.md](./docs/context-index.md)
> Env: [.env.example](./.env.example)
## Apa ini?
Gateway perimeter GeoNet — MikroTik CCR2004 (`10.100.1.1`), NAT publik, dual-WAN hybrid failover, VPN, split-DNS. SSOT config backup & arsitektur.
## Aturan kritis
- SSH: **port 255**, `-m hmac-sha1`, key `~/.ssh/id_rsa_mikrotik` (lihat `server-connection/scripts/ssh-mikrotik.ps1`)
- **Jangan** commit password / `export show-sensitive` — redaksi `nasFtpPass` di backup `.rsc`
- Ubah routing/NAT hanya setelah backup + catat di `docs/version.md`
- DNS publik domain (SPF/DKIM) **bukan** di MikroTik — di registrar
- No commit kecuali user minta
- SSH oleh agent wajib persetujuan user
## Lanjut kerja
```
1. AGENTS.md → docs/ai-session-summary.md → STOP
2. docs/architecture/system-overview.md, docs/server.md, backups/
```
## Pointer
| Butuh | Buka |
|-------|------|
| Arsitektur | [docs/architecture/system-overview.md](./docs/architecture/system-overview.md) |
| NAT mail `.28` | [docs/notes/nat-mail-gisportal.md](./docs/notes/nat-mail-gisportal.md) |
| Restore | [backups/README.md](./backups/README.md) |
| Script deploy | `server-connection/scripts/apply-failover-mikrotik.ps1` |
| Legacy audit | `server-connection/server/mikrotik-audit.txt` |

12
Mikrotik/README.md

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
# MikroTik — Gateway GeoNet
Router perimeter: NAT, dual-WAN hybrid, VPN, split-DNS.
| Item | Nilai |
|------|-------|
| Model | CCR2004-1G-12S+2XS |
| RouterOS | 7.5 |
| SSH | `admin@10.100.1.1:255` + `-m hmac-sha1` |
| Backup repo | `backups/mikrotik-export-*.rsc` |
**AI entry point:** [`docs/context-index.md`](docs/context-index.md)

34
Mikrotik/backups/README.md

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
# MikroTik Config Backups
> **Jangan** commit file yang berisi `password=` / `show-sensitive`.
## File
| File | Tanggal | Keterangan |
|------|---------|------------|
| `mikrotik-export-2026-07-06.rsc` | 2026-07-06 | `/export compact` live dari `10.100.1.1` — password NAS script **diredaksi** |
Backup harian otomatis juga ke QNAP: `mainpve/mikrotik/nas-latest.*` (scheduler `nas-backup-daily`).
## Restore (uji di maintenance window)
```bash
# Via SSH (port 255)
ssh -m hmac-sha1 -i ~/.ssh/id_rsa_mikrotik admin@10.100.1.1 -p 255
# Upload .rsc ke router lalu:
/import file-name=mikrotik-export-2026-07-06.rsc
```
Atau Winbox → Files → upload → Import.
**Peringatan:** import penuh bisa menimpa konfigurasi live. Untuk restore parsial, edit `.rsc` atau gunakan `/system backup load` dari file `.backup` di NAS.
## Regenerate backup ke repo
```powershell
cd server-connection
.\scripts\ssh-mikrotik.ps1 -Command "/export compact"
# Salin output ke Mikrotik/backups/mikrotik-export-YYYY-MM-DD.rsc
# Redaksi nasFtpPass sebelum commit
```

635
Mikrotik/backups/mikrotik-export-2026-07-06.rsc

@ -0,0 +1,635 @@ @@ -0,0 +1,635 @@
# jul/06/2026 15:46:32 by RouterOS 7.5
# software id = E3H0-EVXR
#
# model = CCR2004-1G-12S+2XS
# serial number = HDD08647CP6
/interface bridge
add name=office
add name=server
add add-dhcp-option82=yes dhcp-snooping=yes name=vpn
/interface ethernet
set [ find default-name=ether1 ] comment="ISP BIZNET"
set [ find default-name=sfp-sfpplus1 ] auto-negotiation=no comment=\
"ISP IDBIZ"
set [ find default-name=sfp-sfpplus2 ] comment=DELL-main-netwokrkVMBR0 \
loop-protect=on loop-protect-disable-time=10s
set [ find default-name=sfp-sfpplus3 ] comment=DELL loop-protect=on \
loop-protect-disable-time=10s
set [ find default-name=sfp-sfpplus4 ] comment=IBM loop-protect=on \
loop-protect-disable-time=10s
set [ find default-name=sfp-sfpplus5 ] comment=NAS
set [ find default-name=sfp-sfpplus6 ] auto-negotiation=no disabled=yes
set [ find default-name=sfp-sfpplus10 ] comment="swich server"
set [ find default-name=sfp-sfpplus11 ] comment="swich production" speed=\
10Gbps
set [ find default-name=sfp-sfpplus12 ] comment="swich adm" speed=10Gbps
/interface vlan
add interface=sfp-sfpplus12 loop-protect=on name=vlan3 vlan-id=3
add interface=sfp-sfpplus10 loop-protect=on name=vlan10 vlan-id=10
add interface=sfp-sfpplus11 loop-protect=on name=vlan11 vlan-id=11
add interface=sfp-sfpplus12 loop-protect=on name=vlan12 vlan-id=12
add interface=sfp-sfpplus10 name=vlan99 vlan-id=99
add disabled=yes interface=sfp-sfpplus10 loop-protect=on name=vlan2001 \
vlan-id=2001
add interface=sfp-sfpplus4 name=vlan2004 vlan-id=2001
add interface=sfp-sfpplus10 loop-protect=on name=vlan2010 vlan-id=2010
add interface=sfp-sfpplus12 loop-protect=on name=vlan2012 vlan-id=2012
/interface wireless security-profiles
set [ find default=yes ] supplicant-identity=MikroTik
/ip pool
add name=dhcp_pool0 ranges=192.168.0.2-192.168.0.254
add name=dhcp_pool1 ranges=192.168.0.2-192.168.0.254
add name=dhcp_pool2 ranges=192.168.0.2-192.168.0.254
add name=dhcp_pool3 ranges=192.168.0.2-192.168.0.254
add name=dhcp_pool4 ranges=100.100.1.2-100.100.1.254
add name=dhcp_pool5 ranges=192.168.2.2-192.168.2.254
add name=dhcp_pool6 ranges=192.168.2.2-192.168.2.254
/ip dhcp-server
add address-pool=dhcp_pool3 interface=office lease-time=4w3d name=dhcp1
add address-pool=dhcp_pool4 interface=vpn name=dhcp2
# DHCP server can not run on slave interface!
add address-pool=dhcp_pool6 interface=vlan2004 name=dhcp3 relay=192.168.2.1
/port
set 0 name=serial0
set 1 name=serial1
/queue simple
add name=multimedia priority=1/1 target=192.168.0.4/32
add disabled=yes name=priotiry priority=2/2 target=office
/routing table
add disabled=no fib name="KE ISP 1"
add disabled=no fib name="KE ISP 2"
/snmp community
set [ find default=yes ] write-access=yes
/interface bridge port
add bridge=server interface=sfp-sfpplus2
add bridge=server interface=sfp-sfpplus3
add bridge=server interface=sfp-sfpplus4
add bridge=server interface=sfp-sfpplus5
add bridge=office interface=sfp-sfpplus10 trusted=yes
add bridge=office interface=sfp-sfpplus11 trusted=yes
add bridge=office disabled=yes interface=sfp-sfpplus12
add bridge=server interface=vlan10
add bridge=server interface=vlan11
add bridge=server interface=vlan12
add bridge=server interface=vlan99
add bridge=office interface=vlan2010 trusted=yes
add bridge=server interface=vlan3
add bridge=office interface=vlan2012 trusted=yes
add bridge=office interface=vlan2004 trusted=yes
add bridge=server interface=sfp-sfpplus6
/interface pptp-server server
# PPTP connections are considered unsafe, it is suggested to use a more modern VPN protocol instead
set enabled=yes
/interface sstp-server server
set enabled=yes
/ip address
add address=192.168.88.1/24 comment=defconf disabled=yes interface=ether1 \
network=192.168.88.0
add address=117.102.73.102/29 comment=gisportal.id interface=ether1 network=\
117.102.73.96
add address=117.102.73.101 comment="Not Set" interface=ether1 network=\
117.102.73.101
add address=117.102.73.100 comment=dev.gisportal.id interface=ether1 network=\
117.102.73.100
add address=117.102.73.98 comment=geonet.co.id interface=ether1 network=\
117.102.73.96
add address=10.100.1.1/24 interface=server network=10.100.1.0
add address=117.102.73.99 comment=geonet.co.id interface=ether1 network=\
117.102.73.99
add address=192.168.0.1/24 interface=sfp-sfpplus10 network=192.168.0.0
add address=100.100.1.0 interface=vpn network=100.100.1.0
add address=192.168.2.0/24 interface=vlan2004 network=192.168.2.0
/ip dhcp-client
add add-default-route=no interface=sfp-sfpplus1 use-peer-dns=no
/ip dhcp-server lease
add address=192.168.0.11 client-id=1:bc:fc:e7:8e:55:67 mac-address=\
BC:FC:E7:8E:55:67 server=dhcp1
add address=192.168.0.102 client-id=1:18:fb:7b:a5:50:8b mac-address=\
18:FB:7B:A5:50:8B server=dhcp1
add address=192.168.0.9 client-id=1:18:fd:74:52:8e:5f comment=swich \
mac-address=18:FD:74:52:8E:5F server=dhcp1
add address=192.168.0.8 client-id=1:18:fd:74:8b:b2:7f comment=swich \
mac-address=18:FD:74:8B:B2:7F server=dhcp1
add address=192.168.0.4 mac-address=10:5F:02:84:F0:2F server=dhcp1
add address=192.168.0.10 client-id=1:24:5e:be:42:8f:a5 mac-address=\
24:5E:BE:42:8F:A5 server=dhcp1
add address=192.168.0.74 client-id=1:b4:2e:99:2a:30:4c mac-address=\
B4:2E:99:2A:30:4C server=dhcp1
add address=192.168.0.20 client-id=1:80:3f:5d:f9:c1:40 comment=WAVLINK \
mac-address=80:3F:5D:F9:C1:40 server=dhcp1
add address=192.168.0.57 client-id=1:bc:24:11:5d:58:56 mac-address=\
BC:24:11:5D:58:56 server=dhcp1
add address=192.168.0.13 client-id=1:a0:d3:65:73:c7:97 mac-address=\
A0:D3:65:73:C7:97 server=dhcp1
add address=192.168.0.18 client-id=1:10:ff:e0:3a:7d:15 mac-address=\
10:FF:E0:3A:7D:15 server=dhcp1
add address=192.168.0.64 client-id=1:72:7e:64:31:eb:13 mac-address=\
72:7E:64:31:EB:13 server=dhcp1
add address=192.168.0.80 client-id=1:0:1:f5:2:b:d1 mac-address=\
00:01:F5:02:0B:D1 server=dhcp1
add address=192.168.0.69 mac-address=40:A3:CC:B1:02:15 server=dhcp1
/ip dhcp-server network
add address=100.100.1.0/24 dns-server=100.100.1.1 gateway=100.100.1.1
add address=192.168.0.0/24 dns-server=203.142.82.222,203.142.84.222 gateway=\
192.168.0.1
add address=192.168.2.0/24 dns-server=8.8.8.8,8.8.1.1 gateway=192.168.2.1
/ip dns
set allow-remote-requests=yes servers=203.142.82.222,203.142.84.222
/ip dns static
add address=10.100.1.61 disabled=yes name=arcgisenterprise.gisportal.id
add address=10.100.1.72 name=aernd.gisportal.id
add address=10.100.1.25 name=database.gisportal.id
add address=10.100.1.24 comment="split-dns gisportal apex" name=gisportal.id
add address=10.100.1.24 comment="split-dns dockge" name=dockge.gisportal.id
add address=10.100.1.24 name=geonet.gisportal.id
add address=10.100.1.24 name=api.gisportal.id
add address=10.100.1.25 comment="split-dns database regexp" regexp=\
"^database\\.gisportal\\.id"
add address=10.100.1.72 comment="split-dns aernd regexp" regexp=\
"^aernd\\.gisportal\\.id"
add address=10.100.1.51 comment="split-dns dev regexp" regexp=\
"^dev\\.gisportal\\.id"
add address=10.100.1.24 comment="split-dns gisportal catch-all" regexp=\
"\\.gisportal\\.id"
add address=10.100.1.24 comment="split-dns iguf" name=iguf.geonet.co.id
add address=10.100.1.24 comment=geonetagent-collector name=\
collector.gisportal.id
add address=10.100.1.24 comment=geonetagent-agent name=agent.gisportal.id
/ip firewall address-list
add address=10.100.1.101 list=blocked-internet
add address=10.100.1.100 list=blocked-internet
add address=10.100.1.88 list=blocked-internet
add address=10.100.1.84 list=blocked-internet
add address=10.100.1.91 list=blocked-internet
add address=10.100.1.61 list=blocked-internet
add address=10.100.1.72 list=blocked-internet
add address=10.100.1.80 list=blocked-internet
add address=10.100.1.40 list=blocked-internet
add address=10.100.1.51 list=blocked-internet
add address=10.100.1.25 list=blocked-internet
add address=10.100.1.56 list=blocked-internet
add address=10.100.1.58 list=blocked-internet
add address=10.100.1.55 list=blocked-internet
add address=10.100.1.53 list=blocked-internet
/ip firewall filter
add action=drop chain=forward connection-state=new dst-address-list=ddosed \
src-address-list=ddoser
add action=jump chain=forward connection-state=new jump-target=detect-ddos
add action=return chain=detect-ddos dst-limit=32,32,src-and-dst-addresses/1s
add action=add-dst-to-address-list address-list=ddosed address-list-timeout=\
10m chain=detect-ddos
add action=add-src-to-address-list address-list=ddoser address-list-timeout=\
10m chain=detect-ddos
add action=drop chain=forward comment=block-internet-biznet disabled=yes \
out-interface=ether1 src-address-list=blocked-internet
add action=drop chain=forward comment=block-internet-idbiz disabled=yes \
out-interface=sfp-sfpplus1 src-address-list=blocked-internet
/ip firewall mangle
add action=accept chain=prerouting comment="hybrid server-local-bypass" \
dst-address=10.100.1.0/24 src-address=10.100.1.0/24
add action=accept chain=prerouting comment="hybrid server-to-office-bypass" \
dst-address=192.168.0.0/24 src-address=10.100.1.0/24
add action=accept chain=prerouting comment="hybrid server-to-vpn-bypass" \
dst-address=100.100.1.0/24 src-address=10.100.1.0/24
add action=accept chain=prerouting comment="hybrid mgmt bypass laptop" \
disabled=yes src-address=192.168.0.33
add action=accept chain=prerouting comment="hybrid mgmt bypass laptop" \
src-address=10.100.1.220
add action=accept chain=prerouting comment="hybrid server hairpin bypass" \
dst-address=117.102.73.96/29 src-address=10.100.1.0/24
add action=mark-routing chain=prerouting comment=temp-speedtest-isp2 \
disabled=yes new-routing-mark="KE ISP 2" passthrough=yes src-address=\
10.100.1.24
add action=mark-routing chain=prerouting comment="hybrid server force ISP1" \
new-routing-mark="KE ISP 1" passthrough=no src-address=10.100.1.0/24
add action=mark-connection chain=prerouting connection-mark=no-mark disabled=\
yes in-interface=vlan2004 new-connection-mark="ISP 2" passthrough=yes \
per-connection-classifier=both-addresses-and-ports:2/1
add action=mark-connection chain=prerouting connection-mark=no-mark disabled=\
yes in-interface=server new-connection-mark="ISP 1" passthrough=yes \
per-connection-classifier=both-addresses-and-ports:2/0
add action=mark-routing chain=prerouting connection-mark="ISP 2" disabled=yes \
in-interface=vlan2004 new-routing-mark="KE ISP 2" passthrough=yes \
per-connection-classifier=both-addresses-and-ports:2/1
add action=mark-connection chain=prerouting connection-mark=no-mark disabled=\
yes in-interface=sfp-sfpplus1 new-connection-mark="ISP 2" passthrough=yes
add action=mark-connection chain=prerouting connection-mark=no-mark disabled=\
yes in-interface=ether1 new-connection-mark="ISP 1" passthrough=yes
add action=mark-routing chain=output connection-mark="ISP 2" disabled=yes \
new-routing-mark="KE ISP 2" passthrough=yes
add action=accept chain=prerouting comment="hybrid office-to-server-bypass" \
dst-address=10.100.1.0/24 in-interface=office
add action=accept chain=prerouting comment="hybrid office hairpin bypass" \
dst-address=117.102.73.96/29 in-interface=office
add action=mark-connection chain=prerouting comment="hybrid office PCC ISP1" \
connection-mark=no-mark in-interface=office new-connection-mark="ISP 1" \
passthrough=yes per-connection-classifier=both-addresses-and-ports:2/0
add action=mark-connection chain=prerouting comment="hybrid office PCC ISP2" \
connection-mark=no-mark in-interface=office new-connection-mark="ISP 2" \
passthrough=yes per-connection-classifier=both-addresses-and-ports:2/1
add action=accept chain=prerouting comment="hybrid vpn hairpin bypass" \
dst-address=117.102.73.96/29 src-address=100.100.1.0/24
add action=mark-connection chain=prerouting comment="hybrid vpn PCC ISP1" \
connection-mark=no-mark disabled=yes in-interface=vpn \
new-connection-mark="ISP 1" passthrough=yes per-connection-classifier=\
both-addresses-and-ports:2/0
add action=mark-connection chain=prerouting comment="hybrid vpn PCC ISP2" \
connection-mark=no-mark disabled=yes in-interface=vpn \
new-connection-mark="ISP 2" passthrough=yes per-connection-classifier=\
both-addresses-and-ports:2/1
add action=mark-routing chain=prerouting comment="hybrid route ISP1" \
connection-mark="ISP 1" new-routing-mark="KE ISP 1" passthrough=yes
add action=mark-routing chain=prerouting comment="hybrid route ISP2" \
connection-mark="ISP 2" new-routing-mark="KE ISP 2" passthrough=yes
/ip firewall nat
add action=masquerade chain=srcnat out-interface=ether1
add action=masquerade chain=srcnat out-interface=sfp-sfpplus1
add action=masquerade chain=srcnat
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=80 \
protocol=tcp to-addresses=10.100.1.24 to-ports=80
add action=dst-nat chain=dstnat disabled=yes dst-address=117.102.73.101 \
dst-port=80 protocol=tcp to-addresses=10.100.1.24 to-ports=80
add action=dst-nat chain=dstnat dst-address=117.102.73.101 dst-port=80 \
protocol=tcp to-addresses=10.100.1.76 to-ports=80
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=25 \
protocol=tcp to-addresses=10.100.1.76 to-ports=25
add action=dst-nat chain=dstnat dst-address=117.102.73.101 dst-port=25 \
protocol=tcp to-addresses=10.100.1.76 to-ports=25
add action=dst-nat chain=dstnat dst-address=117.102.73.101 dst-port=143 \
protocol=tcp to-addresses=10.100.1.76 to-ports=143
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=143 \
protocol=tcp to-addresses=10.100.1.76 to-ports=143
add action=dst-nat chain=dstnat dst-address=117.102.73.101 dst-port=993 \
protocol=tcp to-addresses=10.100.1.76 to-ports=993
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=993 \
protocol=tcp to-addresses=10.100.1.76 to-ports=993
add action=dst-nat chain=dstnat dst-address=117.102.73.101 dst-port=587 \
protocol=tcp to-addresses=10.100.1.76 to-ports=587
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=587 \
protocol=tcp to-addresses=10.100.1.76 to-ports=587
add action=dst-nat chain=dstnat disabled=yes dst-address=117.102.73.101 \
dst-port=443 protocol=tcp to-addresses=10.100.1.24 to-ports=443
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=24 \
protocol=tcp to-addresses=10.100.1.24 to-ports=22
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=21 \
protocol=tcp to-addresses=10.100.1.10 to-ports=21
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=443 \
protocol=tcp to-addresses=10.100.1.24 to-ports=443
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=443 \
protocol=tcp to-addresses=10.100.1.24 to-ports=443
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=80 \
protocol=tcp to-addresses=10.100.1.24 to-ports=80
add action=dst-nat chain=dstnat dst-address=117.102.73.100 dst-port=443 \
protocol=tcp to-addresses=10.100.1.51 to-ports=443
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=1433 \
protocol=tcp to-addresses=10.100.1.25 to-ports=1433
add action=dst-nat chain=dstnat comment="rnd SQL Server 2016" dst-address=\
117.102.73.102 dst-port=1434 protocol=tcp to-addresses=10.100.1.75 \
to-ports=1433
add action=dst-nat chain=dstnat comment="Zabbiz agenet active" dst-address=\
117.102.73.102 dst-port=10051 protocol=tcp to-addresses=10.100.1.42 \
to-ports=10051
add action=dst-nat chain=dstnat comment="rnd SQL Server 2022" dst-address=\
117.102.73.102 dst-port=1437 protocol=tcp to-addresses=10.100.1.77 \
to-ports=1433
add action=dst-nat chain=dstnat comment="rnd SQL Server 2016" dst-address=\
117.102.73.102 dst-port=1436 protocol=tcp to-addresses=10.100.1.76 \
to-ports=1433
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=5432 \
protocol=tcp to-addresses=10.100.1.25 to-ports=5432
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=8080 \
protocol=tcp to-addresses=10.100.1.27 to-ports=8080
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=5434 \
protocol=tcp to-addresses=10.100.1.27 to-ports=5434
add action=dst-nat chain=dstnat dst-address=117.102.73.100 dst-port=30443 \
protocol=tcp to-addresses=10.100.1.59 to-ports=30443
add action=dst-nat chain=dstnat dst-address=117.102.73.100 dst-port=6143 \
protocol=tcp to-addresses=10.100.1.59 to-ports=6143
add action=dst-nat chain=dstnat dst-address=117.102.73.100 dst-port=13443 \
protocol=tcp to-addresses=10.100.1.57 to-ports=13443
add action=dst-nat chain=dstnat dst-address=117.102.73.100 dst-port=6443 \
protocol=tcp to-addresses=10.100.1.51 to-ports=6443
add action=dst-nat chain=dstnat dst-address=117.102.73.100 dst-port=80 \
protocol=tcp to-addresses=10.100.1.51 to-ports=80
add action=dst-nat chain=dstnat dst-address=117.102.73.100 dst-port=7443 \
protocol=tcp to-addresses=10.100.1.51 to-ports=7443
add action=dst-nat chain=dstnat dst-address=117.102.73.100 dst-port=2443 \
protocol=tcp to-addresses=10.100.1.51 to-ports=2443
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=5433 \
protocol=tcp to-addresses=10.100.1.27 to-ports=5432
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=25 \
protocol=tcp to-addresses=10.100.1.28 to-ports=25
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=993 \
protocol=tcp to-addresses=10.100.1.28 to-ports=993
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=587 \
protocol=tcp to-addresses=10.100.1.28 to-ports=587
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=465 \
protocol=tcp to-addresses=10.100.1.28 to-ports=465
add action=dst-nat chain=dstnat comment=geonet.co.id disabled=yes \
dst-address=117.102.73.99 dst-port=25 protocol=tcp to-addresses=\
10.100.1.40 to-ports=25
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=995 \
protocol=tcp to-addresses=10.100.1.40 to-ports=995
add action=dst-nat chain=dstnat comment=geonet.co.id disabled=yes \
dst-address=117.102.73.99 dst-port=995 protocol=tcp to-addresses=\
10.100.1.40 to-ports=995
add action=dst-nat chain=dstnat disabled=yes dst-address=117.102.73.102 \
dst-port=25 protocol=udp to-addresses=10.100.1.40 to-ports=25
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=587 \
protocol=tcp to-addresses=10.100.1.40 to-ports=587
add action=dst-nat chain=dstnat comment=geonet.co.id disabled=yes \
dst-address=117.102.73.99 dst-port=587 protocol=tcp to-addresses=\
10.100.1.40 to-ports=587
add action=dst-nat chain=dstnat disabled=yes dst-address=117.102.73.102 \
dst-port=587 protocol=udp to-addresses=10.100.1.40 to-ports=587
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=389 \
protocol=tcp to-addresses=10.100.1.40 to-ports=389
add action=dst-nat chain=dstnat comment=geonet.co.id disabled=yes \
dst-address=117.102.73.99 dst-port=389 protocol=tcp to-addresses=\
10.100.1.40 to-ports=389
add action=dst-nat chain=dstnat disabled=yes dst-address=117.102.73.102 \
dst-port=389 protocol=udp to-addresses=10.100.1.40 to-ports=389
add action=dst-nat chain=dstnat disabled=yes dst-address=117.102.73.102 \
dst-port=143 protocol=udp to-addresses=10.100.1.40 to-ports=143
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=143 \
protocol=tcp to-addresses=10.100.1.40 to-ports=143
add action=dst-nat chain=dstnat comment=geonet.co.id disabled=yes \
dst-address=117.102.73.99 dst-port=143 protocol=tcp to-addresses=\
10.100.1.40 to-ports=143
add action=dst-nat chain=dstnat disabled=yes dst-address=117.102.73.102 \
dst-port=110 protocol=udp to-addresses=10.100.1.40 to-ports=110
add action=dst-nat chain=dstnat dst-address=117.102.73.102 dst-port=110 \
protocol=tcp to-addresses=10.100.1.40 to-ports=110
add action=dst-nat chain=dstnat comment=geonet.co.id disabled=yes \
dst-address=117.102.73.99 dst-port=110 protocol=tcp to-addresses=\
10.100.1.40 to-ports=110
add action=masquerade chain=srcnat comment="Hairpin NAT" dst-address=\
10.100.1.24 dst-port=443 out-interface=ether1 protocol=tcp src-address=\
192.168.0.0/24
add action=masquerade chain=srcnat comment="Hairpin NAT" dst-address=\
10.100.1.100 dst-port=443 out-interface=ether1 protocol=tcp src-address=\
192.168.0.0/24
add action=masquerade chain=srcnat comment="Hairpin NAT" dst-address=\
10.100.1.24 dst-port=80 out-interface=ether1 protocol=tcp src-address=\
192.168.0.0/24
add action=dst-nat chain=dstnat dst-address=117.102.73.101 dst-port=995 \
protocol=tcp to-addresses=10.100.1.76 to-ports=995
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=995 \
protocol=tcp to-addresses=10.100.1.76 to-ports=995
add action=dst-nat chain=dstnat dst-address=117.102.73.101 dst-port=465 \
protocol=tcp to-addresses=10.100.1.76 to-ports=465
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=465 \
protocol=tcp to-addresses=10.100.1.76 to-ports=465
add action=dst-nat chain=dstnat dst-address=117.102.73.99 dst-port=993 \
protocol=tcp to-addresses=10.100.1.76 to-ports=993
add action=masquerade chain=srcnat comment=hairpin-10.100.1-nginx-443 \
dst-address=10.100.1.24 dst-port=443 protocol=tcp src-address=\
10.100.1.0/24
add action=masquerade chain=srcnat comment=hairpin-10.100.1-nginx-80 \
dst-address=10.100.1.24 dst-port=80 protocol=tcp src-address=\
10.100.1.0/24
add action=masquerade chain=srcnat comment=hairpin-vpn-nginx-443 dst-address=\
10.100.1.24 dst-port=443 protocol=tcp src-address=100.100.1.0/24
add action=masquerade chain=srcnat comment=hairpin-vpn-nginx-80 dst-address=\
10.100.1.24 dst-port=80 protocol=tcp src-address=100.100.1.0/24
add action=dst-nat chain=dstnat comment=ssh-mikrotik-public-102 dst-address=\
117.102.73.102 dst-port=255 protocol=tcp to-addresses=10.100.1.1 \
to-ports=255
add action=dst-nat chain=dstnat comment=ssh-nginx-public-22 disabled=yes \
dst-address=117.102.73.102 dst-port=22 protocol=tcp to-addresses=\
10.100.1.24 to-ports=22
add action=masquerade chain=srcnat comment=hairpin-ssh-mikrotik dst-address=\
10.100.1.1 dst-port=255 protocol=tcp src-address=10.100.1.0/24
add action=masquerade chain=srcnat comment=hairpin-ssh-nginx disabled=yes \
dst-address=10.100.1.24 dst-port=22 protocol=tcp src-address=\
10.100.1.0/24
add action=masquerade chain=srcnat comment=hairpin-ssh-mikrotik-office \
dst-address=10.100.1.1 dst-port=255 protocol=tcp src-address=\
192.168.0.0/24
add action=masquerade chain=srcnat comment=hairpin-ssh-nginx-office disabled=\
yes dst-address=10.100.1.24 dst-port=22 protocol=tcp src-address=\
192.168.0.0/24
add action=dst-nat chain=dstnat comment=ssh-public-db dst-address=\
117.102.73.102 dst-port=225 protocol=tcp to-addresses=10.100.1.25 \
to-ports=22
add action=masquerade chain=srcnat comment=hairpin-ssh-public-db dst-address=\
10.100.1.25 dst-port=22 protocol=tcp src-address=10.100.1.0/24
add action=masquerade chain=srcnat comment=hairpin-ssh-public-db-office \
dst-address=10.100.1.25 dst-port=22 protocol=tcp src-address=\
192.168.0.0/24
/ip route
add check-gateway=ping comment="hybrid failover ISP1 primary" disabled=no \
distance=1 dst-address=0.0.0.0/0 gateway=117.102.73.97 pref-src="" \
routing-table=main scope=30 suppress-hw-offload=no target-scope=10
add check-gateway=none comment="hybrid ISP2" disabled=no distance=1 \
dst-address=0.0.0.0/0 gateway=192.168.1.1 pref-src="" routing-table=\
"KE ISP 2" scope=30 suppress-hw-offload=no target-scope=10
add check-gateway=ping comment="hybrid failover ISP2 backup" disabled=no \
distance=2 dst-address=0.0.0.0/0 gateway=192.168.1.1 routing-table=main
add check-gateway=none comment="hybrid ISP1" disabled=no distance=1 \
dst-address=0.0.0.0/0 gateway=117.102.73.97 routing-table="KE ISP 1"
add comment="hybrid ISP1 WAN lan" distance=1 dst-address=117.102.73.96/29 \
gateway=ether1 routing-table="KE ISP 1"
add comment="hybrid ISP2 WAN lan" distance=1 dst-address=192.168.1.0/24 \
gateway=sfp-sfpplus1 routing-table="KE ISP 2"
add comment="hybrid office lan ISP1" distance=1 dst-address=192.168.0.0/24 \
gateway=office routing-table="KE ISP 1"
add comment="hybrid office lan ISP2" distance=1 dst-address=192.168.0.0/24 \
gateway=office routing-table="KE ISP 2"
/ip service
set telnet disabled=yes
set ftp disabled=yes
set www disabled=yes
set ssh port=255
set api disabled=yes
set winbox address=10.100.1.0/24,192.168.0.0/24
set api-ssl disabled=yes
/ppp aaa
set use-radius=yes
/ppp secret
add local-address=10.100.1.1 name=rbsetiawan remote-address=10.100.1.220
add local-address=10.100.1.1 name=asobari remote-address=10.100.1.225
add local-address=10.100.1.1 name=uabdurrohman remote-address=10.100.1.226
add local-address=10.100.1.1 name=trachman remote-address=10.100.1.227
add local-address=10.100.1.1 name=test remote-address=10.100.1.211
add local-address=10.100.1.1 name=dpradana remote-address=10.100.1.228
add local-address=10.100.1.1 name=ataufik remote-address=10.100.1.229
add local-address=10.100.1.1 name=awrahajeng remote-address=10.100.1.230
/radius
add address=10.100.1.40 domain=gisportal.id service=ppp src-address=\
10.100.1.1
/routing rule
add action=lookup comment="hybrid ISP1" routing-mark="KE ISP 1" table=\
"KE ISP 1"
add action=lookup comment="hybrid ISP2" routing-mark="KE ISP 2" table=\
"KE ISP 2"
/snmp
set enabled=yes
/system clock
set time-zone-name=Asia/Jakarta
/system scheduler
add comment="hybrid scheduler" interval=1m name=hybrid-sync-mode on-event=\
hybrid-sync-mode policy=\
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
start-date=jun/13/2026 start-time=17:41:40
add comment="Monitor latency ISP1 vs ISP2" interval=2m name=latency-monitor \
on-event=latency-monitor policy=\
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
start-time=startup
add comment="Backup config ke QNAP 10.100.1.10" interval=1d name=\
nas-backup-daily on-event=nas-backup policy=\
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
start-date=jun/15/2026 start-time=03:00:00
/system script
add dont-require-permissions=yes name=hybrid-set-mode-both-up owner=admin \
policy=read,write,test source="\r\
\n/ip firewall mangle enable [find comment=\"hybrid office PCC ISP1\"]\r\
\n/ip firewall mangle enable [find comment=\"hybrid office PCC ISP2\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid vpn PCC ISP1\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid vpn PCC ISP2\"]\r\
\n/ip firewall mangle enable [find comment=\"hybrid route ISP1\"]\r\
\n/ip firewall mangle enable [find comment=\"hybrid route ISP2\"]\r\
\n:log info \"HYBRID: mode BOTH-UP (office PCC 50/50, VPN failover only)\"\
\r\
\n"
add dont-require-permissions=yes name=hybrid-set-mode-isp1-down owner=admin \
policy=read,write,test source="\r\
\n/ip firewall mangle disable [find comment=\"hybrid office PCC ISP1\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid office PCC ISP2\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid vpn PCC ISP1\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid vpn PCC ISP2\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid route ISP1\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid route ISP2\"]\r\
\n:log info \"HYBRID: mode ISP1-DOWN\"\r\
\n"
add dont-require-permissions=yes name=hybrid-set-mode-isp2-down owner=admin \
policy=read,write,test source="\r\
\n/ip firewall mangle disable [find comment=\"hybrid office PCC ISP2\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid vpn PCC ISP2\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid route ISP2\"]\r\
\n/ip firewall mangle enable [find comment=\"hybrid office PCC ISP1\"]\r\
\n/ip firewall mangle disable [find comment=\"hybrid vpn PCC ISP1\"]\r\
\n/ip firewall mangle enable [find comment=\"hybrid route ISP1\"]\r\
\n:log info \"HYBRID: mode ISP2-DOWN (office ISP1 only, VPN failover only)\
\"\r\
\n"
add dont-require-permissions=yes name=hybrid-sync-mode owner=admin policy=\
read,write,test,sniff source="\r\
\n:local up1 false\r\
\n:local up2 false\r\
\n:local ok1 0\r\
\n:local ok2 0\r\
\n:for i from=1 to=3 do={\r\
\n :local r1 [/ping 117.102.73.97 count=1 as-value]\r\
\n :if ([:typeof (\$r1->\"time\")] != \"nothing\") do={ :set ok1 (\$ok1 +\
\_1) }\r\
\n :local r2 [/ping 192.168.1.1 count=1 as-value]\r\
\n :if ([:typeof (\$r2->\"time\")] != \"nothing\") do={ :set ok2 (\$ok2 +\
\_1) }\r\
\n}\r\
\n:if (\$ok1 >= 2) do={ :set up1 true }\r\
\n:if (\$ok2 >= 2) do={ :set up2 true }\r\
\n:if (\$up1 && \$up2) do={\r\
\n /system script run hybrid-set-mode-both-up\r\
\n} else={\r\
\n :if (!\$up1) do={\r\
\n /system script run hybrid-set-mode-isp1-down\r\
\n } else={\r\
\n :if (!\$up2) do={\r\
\n /system script run hybrid-set-mode-isp2-down\r\
\n }\r\
\n }\r\
\n}\r\
\n"
add dont-require-permissions=yes name=latency-monitor owner=admin policy=\
read,write,test,sniff source="\r\
\n:local hysteresis 10ms\r\
\n:global bestWan\r\
\n:global isp1Latency\r\
\n:global isp2Latency\r\
\n:global isp1Loss\r\
\n:global isp2Loss\r\
\n:if ([:typeof \$bestWan] = \"nothing\") do={ :set bestWan \"isp1\" }\r\
\n:local t1 0ms\r\
\n:local t2 0ms\r\
\n:local ok1 0\r\
\n:local ok2 0\r\
\n:local i 0\r\
\n:do {\r\
\n :local r1 [/ping 117.102.73.97 count=1 as-value]\r\
\n :if ([:typeof (\$r1->\"time\")] != \"nothing\") do={\r\
\n :set t1 (\$t1 + (\$r1->\"time\"))\r\
\n :set ok1 (\$ok1 + 1)\r\
\n }\r\
\n :local r2 [/ping 192.168.1.1 count=1 as-value]\r\
\n :if ([:typeof (\$r2->\"time\")] != \"nothing\") do={\r\
\n :set t2 (\$t2 + (\$r2->\"time\"))\r\
\n :set ok2 (\$ok2 + 1)\r\
\n }\r\
\n :set i (\$i + 1)\r\
\n} while=(\$i < 5)\r\
\n:local avg1 9999ms\r\
\n:local avg2 9999ms\r\
\n:local loss1 100\r\
\n:local loss2 100\r\
\n:if (\$ok1 > 0) do={\r\
\n :set avg1 (\$t1 / \$ok1)\r\
\n :set loss1 (100 - ((\$ok1 * 100) / 5))\r\
\n}\r\
\n:if (\$ok2 > 0) do={\r\
\n :set avg2 (\$t2 / \$ok2)\r\
\n :set loss2 (100 - ((\$ok2 * 100) / 5))\r\
\n}\r\
\n:set isp1Latency \$avg1\r\
\n:set isp2Latency \$avg2\r\
\n:set isp1Loss \$loss1\r\
\n:set isp2Loss \$loss2\r\
\n:local newBest \$bestWan\r\
\n:if (\$loss1>49 && \$loss2>49) do={\r\
\n :log warning \"Latency Monitor: kedua ISP loss tinggi, tetap \$bestWan\
\"\r\
\n} else={\r\
\n :if (\$loss2>49) do={ :set newBest \"isp1\" } else={\r\
\n :if (\$loss1>49) do={ :set newBest \"isp2\" } else={\r\
\n :if (\$avg1 < (\$avg2 - \$hysteresis)) do={ :set newBest \"isp1\" \
} else={\r\
\n :if (\$avg1 > (\$avg2 + \$hysteresis)) do={ :set newBest \"isp2\
\" }\r\
\n }\r\
\n }\r\
\n }\r\
\n :set bestWan \$newBest\r\
\n}\r\
\n:log info (\"Latency Monitor: ISP1=\" . [:tostr \$avg1] . \" loss=\" . \
\$loss1 . \"% | ISP2=\" . [:tostr \$avg2] . \" loss=\" . \$loss2 . \"% | b\
est=\" . \$bestWan)\r\
\n"
add dont-require-permissions=yes name=nas-backup owner=admin policy=\
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\
\r\
\n:global nasFtpHost \"10.100.1.10\"\r\
\n:global nasFtpUser \"admin\"\r\
\n:global nasFtpPass \"REDACTED_SEE_NAS_VAULT\"\r\
\n:global nasFtpPath \"mainpve/mikrotik\"\r\
\n:local bname \"nas-latest\"\r\
\n:log info \"NAS backup: mulai\"\r\
\n/system backup save name=\$bname\r\
\n/export file=\$bname\r\
\n:delay 2s\r\
\n/tool fetch address=\$nasFtpHost mode=ftp user=\$nasFtpUser password=\$n\
asFtpPass src-path=(\$bname . \".backup\") dst-path=(\$nasFtpPath . \"/\" \
. \$bname . \".backup\") upload=yes\r\
\n/tool fetch address=\$nasFtpHost mode=ftp user=\$nasFtpUser password=\$n\
asFtpPass src-path=(\$bname . \".rsc\") dst-path=(\$nasFtpPath . \"/\" . \
\$bname . \".rsc\") upload=yes\r\
\n/file remove (\$bname . \".backup\")\r\
\n/file remove (\$bname . \".rsc\")\r\
\n:log info \"NAS backup: selesai\"\r\
\n"
/tool netwatch
add comment="hybrid monitor ISP1" down-script=hybrid-set-mode-isp1-down host=\
117.102.73.97 interval=10s timeout=3s type=simple up-script=\
hybrid-sync-mode
add comment="hybrid monitor ISP2" down-script=hybrid-set-mode-isp2-down host=\
192.168.1.1 interval=10s timeout=3s type=simple up-script=\
hybrid-sync-mode

3
Mikrotik/docs/adr/000-template.md

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
# ADR Template
Salin untuk keputusan baru. Contoh: [ADR-0001-hybrid-dual-wan.md](./ADR-0001-hybrid-dual-wan.md).

36
Mikrotik/docs/adr/ADR-0001-hybrid-dual-wan.md

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
# ADR-0001: Hybrid Dual-WAN (failover + PCC office)
| Field | Value |
|-------|-------|
| Status | Accepted (live) |
| Date | 2026-06-13 (verified 2026-07-06) |
---
## Context
Dua ISP: Biznet (ether1, IP publik statik) dan IDBiz (sfp-sfpplus1, DHCP). Butuh internet user kantor tetap jalan saat satu ISP down.
---
## Decision
- **Failover** default route main: ISP1 `check-gateway=ping`, backup ISP2
- **Hybrid PCC** untuk office: scheduler `hybrid-sync-mode` + netwatch ISP1/ISP2
- Routing tables `KE ISP 1` / `KE ISP 2` + mangle marks
- Script deploy: `server-connection/scripts/apply-hybrid-mikrotik.ps1`
---
## Consequences
**Positif:** Resiliensi ISP; office load-balance saat both up
**Negatif:** Kompleksitas troubleshooting; perubahan salah bisa putus internet seluruh kantor
---
## References
- `server-connection/server/existing-network-geonet.txt`
- `backups/mikrotik-export-2026-07-06.rsc`

38
Mikrotik/docs/ai-session-summary.md

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
# AI Session Summary
```yaml
session: 2026-07-06-mikrotik-bootstrap-audit
project: Mikrotik/
version: 0.1.0
status: ready
next: Verifikasi NAT mail gisportal.id vs Mailcow .28 setelah perubahan DNS
agent: Cursor
```
## TL;DR
- Bootstrap repo `Mikrotik/` + **audit live** `10.100.1.1` (RouterOS 7.5, uptime ~1d)
- Mode jaringan: **hybrid dual-WAN** (ISP1 Biznet + ISP2 IDBiz) + failover `check-gateway=ping`
- Backup `/export compact``backups/mikrotik-export-2026-07-06.rsc` (NAS FTP pass **diredaksi**)
- Mail publik `117.102.73.102` → NAT ke Mailcow `10.100.1.28` (:25/:587/:993/:465)
- Chat sebelumnya: user tanya riwayat MikroTik — ada di `server-connection/`, belum di chat Cursor
## Lanjut dari sini
1. Dokumentasi NAT mail selaras Mailcow `gisportal.id` — [notes/nat-mail-gisportal.md](./notes/nat-mail-gisportal.md)
2. Sebelum ubah routing: backup baru + append `version.md`
3. Pertimbangkan nonaktifkan PPTP (legacy)
## Jangan
- Commit `nasFtpPass` atau PPP password plaintext
- Edit SPF/DKIM di MikroTik
- Import `.rsc` ke production tanpa maintenance window
## Konteks chat (disimpan)
| Topik | Ringkasan |
|-------|-----------|
| Riwayat MikroTik di chat | Belum troubleshoot eksplisit di Cursor; infra ada di `server-connection/server/` |
| Mailcow gisportal | MX langsung `.102`; NAT port email ke `.28` — DNS SPF di Master Web |
| SSH gagal tanpa key | `Permission denied` — wajib `id_rsa_mikrotik` |

128
Mikrotik/docs/architecture/system-overview.md

@ -0,0 +1,128 @@ @@ -0,0 +1,128 @@
# System Overview — MikroTik Gateway
**Last updated:** 2026-07-06
**Source:** Live SSH audit + `backups/mikrotik-export-2026-07-06.rsc`
---
## Perangkat
| Item | Nilai |
|------|-------|
| Hostname | MikroTik |
| Model | CCR2004-1G-12S+2XS (ARM64, 4 CPU) |
| RouterOS | 7.5 stable |
| RAM | 4032 MiB (~3820 free) |
| Serial | HDD08647CP6 |
---
## Diagram tingkat tinggi
```
Internet
┌──────────────┴──────────────┐
│ ISP1 Biznet (ether1) │ ISP2 IDBiz (sfp-sfpplus1, DHCP)
│ GW 117.102.73.97 │ GW 192.168.1.1
└──────────────┬──────────────┘
┌─────────▼─────────┐
│ MikroTik .1 │
│ hybrid failover │
│ + PCC office │
└─────────┬─────────┘
┌───────────────┼───────────────┐
│ │ │
bridge:server bridge:office bridge:vpn
10.100.1.0/24 192.168.0.0/24 100.100.1.0/24
│ │ │
Proxmox, DB, User kantor, SSTP/PPTP
Mailcow .28, MULTIMEDIA .14 RADIUS→AD .40
nginx .24, ...
```
---
## Segment jaringan
| Segment | Gateway | Bridge / IF | DHCP |
|---------|---------|-------------|------|
| Server LAN | `10.100.1.1` | `server` | — |
| Office | `192.168.0.1` | `office` (sfp-sfpplus10) | dhcp1, lease 4w3d |
| VPN remote | `100.100.1.0` | `vpn` | dhcp2 |
| IBM (legacy) | `192.168.2.1` | vlan2004 | relay (inactive) |
---
## WAN publik (blok `117.102.73.96/29`)
| IP | Komentar | Fungsi utama |
|----|----------|--------------|
| `.102` | gisportal.id | Web/API/mail NAT → internal |
| `.99` | geonet.co.id | HTTP/S → `.24`; beberapa mail rule legacy |
| `.98` | geonet.co.id | — |
| `.100` | dev.gisportal.id | Dev/ArcGIS NAT |
| `.101` | Not Set | Mail legacy → `.76` |
---
## Routing (aktif 2026-07-06)
| Route | Tabel | Gateway | Catatan |
|-------|-------|---------|---------|
| `0.0.0.0/0` | main | `117.102.73.97` | **Primary**, `check-gateway=ping` |
| `0.0.0.0/0` | KE ISP 1/2 | hybrid tables | PCC / failover scripts |
| ISP2 WAN | sfp-sfpplus1 | `192.168.1.4/24` | DHCP dynamic — **UP** |
Scheduler `hybrid-sync-mode` + netwatch ping ISP1/ISP2 menyesuaikan mangle PCC.
---
## NAT penting (publik → internal)
| Publik | Port | Internal | Service |
|--------|------|----------|---------|
| `.102` | 80, 443 | `10.100.1.24` | nginx reverse proxy |
| `.102` | 25, 587, 993, 465 | `10.100.1.28` | **Mailcow** |
| `.102` | 5432, 1433, … | `.25`, `.75`, … | DB / SQL (hati-hati expose) |
| `.99` | 80, 443 | `10.100.1.24` | geonet web |
| `.100` | 443, 6443, … | `.51`, `.57`, `.59` | ArcGIS dev |
Mail **geonet.co.id** masih ada rule ke `.76` / `.40` (legacy cPanel/AD) — beberapa **disabled**.
---
## VPN & auth
| Protokol | Status | Auth |
|----------|--------|------|
| SSTP | enabled (:443) | RADIUS → `10.100.1.40` |
| PPTP | enabled ⚠ | Legacy — disarankan matikan |
| PPP secrets | lokal + RADIUS | IP tetap `10.100.1.220–230` |
---
## DNS
- Forwarder: `203.142.82.222`, `203.142.84.222`
- **Split-DNS** static: `*.gisportal.id``10.100.1.24` (user office/VPN resolve internal)
---
## Layanan management
| Service | Port | Akses |
|---------|------|-------|
| SSH | **255** | Key RSA |
| Winbox | default | `10.100.1.0/24`, `192.168.0.0/24` |
| WWW/API | disabled | — |
---
## Backup otomatis
| Target | Jadwal |
|--------|--------|
| QNAP `10.100.1.10:mainpve/mikrotik/` | Daily 03:00 (scheduler) |
| Repo `Mikrotik/backups/` | Manual / sesi audit |

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save