#pConfigFile
कॉन्फ़िगरेशन एज़ कोड: export, plan, apply और Git पाइपलाइन
Git में YAML फ़ाइलों के रूप में पूरी सर्वर स्थिति: टेनेंट, डोमेन (DKIM सेलेक्टर, MTA-STS, रूटिंग), उपनाम, अग्रेषण और स्वचालित उत्तर सहित मेलबॉक्स, वितरण सूचियाँ, साझा मेलबॉक्स, कक्ष, GAL और संसाधन नियम, साझाकरण और प्रत्यायोजन, SMTP, स्मार्ट होस्ट, रूट, ट्रांसपोर्ट नियम, स्पैम और वायरस सुरक्षा, फ़ायरवॉल और ऑटो-बैन, TLS, पहचान स्रोत, API कुंजियाँ (केवल मेटाडेटा), वेबहुक, सूचनाएँ, ऑप्स अलर्ट, POP3, फ़ाइलें, बुकिंग, आर्काइव और DNS फेलओवर। nextxch-cli config plan, terraform plan की तरह दिखाता है कि क्या बनाया, बदला और हटाया जाएगा; apply पहले सब कुछ जाँचता है और फिर उसे एक निश्चित क्रम में लागू करता है। एडमिन: सिस्टम > फ़ाइल के रूप में कॉन्फ़िगरेशन।
सीक्रेट कभी फ़ाइल में नहीं जाते और कभी निर्यात नहीं होते: पासवर्ड, टोकन और कुंजियाँ secretRef: {env: NAME} या secretRef: {file: path} के रूप में लिखी जाती हैं और apply के समय रिज़ॉल्व होती हैं (CI रनर पर nextxch-cli द्वारा; सर्वर पर केवल NEXTXCH_SECRET_* वेरिएबल और <data>/secrets/ के नीचे की फ़ाइलें)। DKIM कुंजियाँ, प्रमाणपत्र और पासवर्ड हैश सर्वर पर ही रहते हैं। API v1 की तरह Business सुविधा; JSON Schema निःशुल्क है।
1. फ़ाइल प्रारूप: apiVersion, kind, metadata.name, spec
कई दस्तावेज़ों वाली एक फ़ाइल (--- से अलग) या प्रति क्षेत्र एक फ़ाइल वाला फ़ोल्डर (config export --split)। फ़ाइल में न मौजूद फ़ील्ड सर्वर पर जैसे हैं वैसे ही रहते हैं; सूचियाँ (उपनाम, सदस्य) पूरी इकाई के रूप में गिनी जाती हैं।
# yaml-language-server: $schema=https://nextxch.com/schemas/nextxch-config-v1.schema.json
apiVersion: nextxch/v1
kind: Manifest # optional: areas in which --prune may delete
metadata: {name: nextxch}
spec: {areas: [domains, accounts, sharing, transport]}
---
apiVersion: nextxch/v1
kind: Domain
metadata: {name: company.com}
spec:
tenant: default
catchAll: info@company.com
dkim: {mode: dual, rsaSelector: s2026, ed25519Selector: ed2026} # keys stay on the server
mtaSts: {mode: enforce, maxAgeSeconds: 604800, mxHosts: [mail.company.com]}
---
apiVersion: nextxch/v1
kind: Account
metadata: {name: anna.smith@company.com}
spec:
displayName: Anna Smith
role: User # User, TenantAdmin, ServerAdmin, ComplianceAuditor
quotaMb: 10240
aliases: [anna@company.com]
forwarding: {address: anna@private.example, keepCopy: true}
outOfOffice: {enabled: false}
password: {secretRef: {env: ANNA_PASSWORD}} # never the password itself
---
apiVersion: nextxch/v1
kind: Account
metadata: {name: info@company.com}
spec: {type: shared, displayName: Info mailbox} # user, shared, room, equipment
---
apiVersion: nextxch/v1
kind: Share
metadata: {name: "info@company.com/mailbox -> anna.smith@company.com"}
spec: {permissions: [read, write, sendAs]}
---
apiVersion: nextxch/v1
kind: SmtpSettings
metadata: {name: server} # server-wide settings are always named "server"
spec:
outboundSmartHost: smtp.provider.example
outboundSmartPort: 587
smartHostPassword: {secretRef: {file: secrets/smarthost.txt}}
2. nextxch-cli के साथ वर्कफ़्लो
पहले निर्यात और कमिट करें, फिर पुल रिक्वेस्ट के माध्यम से बदलें। plan आउटपुट का उदाहरण:
export NEXTXCH_URL=https://mail.company.com
export NEXTXCH_API_KEY=smk_... # key with config.read or config.write
nextxch-cli config export --split nextxch/
nextxch-cli config validate -f nextxch/ # offline: kinds and fields
nextxch-cli config plan -f nextxch/
~ Domain company.com
mtaSts.mode: "testing" -> "enforce"
~ Account anna.smith@company.com
quotaMb: 10240 -> 20480
aliases: ["anna@company.com"] -> ["a.smith@company.com","anna@company.com"]
~ password: (secret changed)
+ Account info@company.com
type: "shared"
displayName: "Info mailbox"
Not in the file (1), deleted only with --prune:
Account old@company.com
Plan: 1 to create, 2 to update, 0 to delete, 41 unchanged.
# exit code 2 = changes
nextxch-cli config apply -f nextxch/ --prune --yes
nextxch-cli config history
3. GitHub Actions: पुल रिक्वेस्ट पर plan, मर्ज पर apply
comment चरण सहित पूरी फ़ाइल: रिपॉज़िटरी में deploy/gitops/github-actions.yml। API कुंजी रिपॉज़िटरी secrets से आती है, कभी फ़ाइल से नहीं।
name: nextxch config
on:
pull_request: {paths: ["nextxch/**"]}
push: {branches: [main], paths: ["nextxch/**"]}
permissions: {contents: read, pull-requests: write}
concurrency: {group: nextxch-config, cancel-in-progress: false}
env: {NEXTXCH_IMAGE: "nextxch/server:latest"}
jobs:
plan:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Plan
env:
NEXTXCH_URL: ${{ vars.NEXTXCH_URL }}
NEXTXCH_API_KEY: ${{ secrets.NEXTXCH_CONFIG_READ_KEY }}
run: |
set +e
docker run --rm -e NEXTXCH_URL -e NEXTXCH_API_KEY -e NO_COLOR=1 -v "$PWD:/work" -w /work \
--entrypoint nextxch-cli "$NEXTXCH_IMAGE" config plan -f nextxch/ --prune > plan.txt 2>&1
code=$?; cat plan.txt; [ "$code" -ne 1 ]
# danach: actions/github-script postet plan.txt als Kommentar (siehe deploy/gitops)
apply:
if: github.event_name == 'push'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- env:
NEXTXCH_URL: ${{ vars.NEXTXCH_URL }}
NEXTXCH_API_KEY: ${{ secrets.NEXTXCH_CONFIG_WRITE_KEY }}
SMARTHOST_PASSWORD: ${{ secrets.SMARTHOST_PASSWORD }}
run: |
docker run --rm -e NEXTXCH_URL -e NEXTXCH_API_KEY -e SMARTHOST_PASSWORD -v "$PWD:/work" -w /work \
--entrypoint nextxch-cli "$NEXTXCH_IMAGE" config apply -f nextxch/ --prune --yes
4. GitLab CI: merge request नोट के रूप में plan, डिफ़ॉल्ट ब्रांच पर apply
पूरी फ़ाइल: deploy/gitops/gitlab-ci.yml। कुंजियाँ मास्क्ड, संरक्षित CI/CD वेरिएबल हैं; resource_group एक साथ दो apply को रोकता है।
stages: [validate, plan, apply]
variables: {CONFIG_DIR: nextxch, NO_COLOR: "1"}
.nextxch-cli:
image: {name: "nextxch/server:latest", entrypoint: [""]}
validate:
extends: .nextxch-cli
stage: validate
script: [nextxch-cli config validate -f "$CONFIG_DIR"]
plan:
extends: .nextxch-cli
stage: plan
rules: [{if: '$CI_PIPELINE_SOURCE == "merge_request_event"'}]
variables: {NEXTXCH_API_KEY: $NEXTXCH_CONFIG_READ_KEY}
script:
- set +e
- nextxch-cli config plan -f "$CONFIG_DIR" --prune > plan.txt 2>&1; code=$?
- cat plan.txt; test "$code" -ne 1
artifacts: {when: always, paths: [plan.txt]}
# plan-comment: curl auf /merge_requests/:iid/notes mit plan.txt (siehe deploy/gitops)
apply:
extends: .nextxch-cli
stage: apply
resource_group: nextxch-config
environment: production
rules: [{if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'}]
variables: {NEXTXCH_API_KEY: $NEXTXCH_CONFIG_WRITE_KEY}
script: [nextxch-cli config apply -f "$CONFIG_DIR" --prune --yes]
5. एडिटर में JSON Schema और REST कॉल
VS Code (Red Hat "YAML" एक्सटेंशन) और JetBrains IDE टाइप करते समय फ़ाइलों की जाँच करते हैं। हर सर्वर /api/v1/config/schema पर स्कीमा भी प्रदान करता है।
# .vscode/settings.json
{
"yaml.schemas": {
"https://nextxch.com/schemas/nextxch-config-v1.schema.json": ["nextxch/*.yaml"]
}
}
# Plan ohne CLI, direkt per REST (YAML als Body):
curl -s -X POST "$NEXTXCH_URL/api/v1/config/plan?prune=true" \
-H "Authorization: Bearer $NEXTXCH_API_KEY" \
-H "Content-Type: application/yaml" --data-binary @nextxch.yaml
6. सूचियों में सीक्रेट और बिना रीस्टार्ट ACME
पाइपलाइन से ऑथ हेडर और HMAC सीक्रेट के साथ MTA हुक, webhook सीक्रेट के साथ अलर्ट लक्ष्य और DNS-01 के साथ Let's Encrypt पर स्विच: एक apply, कोई रीस्टार्ट नहीं।
apiVersion: nextxch/v1
kind: MtaFilters
metadata: {name: server}
spec:
hooks:
- name: spamcheck
url: https://hooks.company.com/check
stages: [data]
authHeaderName: Authorization
authHeaderValue: {secretRef: {env: HOOK_TOKEN}} # in the plan: hooks[spamcheck].authHeaderValue
hmacSecret: {secretRef: {file: secrets/hook-hmac.txt}}
---
apiVersion: nextxch/v1
kind: OpsAlerts
metadata: {name: server}
spec:
targets:
- name: pager
type: webhook
address: https://pager.company.com/hook
secret: {secretRef: {env: PAGER_SECRET}}
---
apiVersion: nextxch/v1
kind: TlsSettings
metadata: {name: server}
spec:
acme:
enabled: true
directoryUrl: https://acme-v02.api.letsencrypt.org/directory
contactEmail: postmaster@company.com
challenge: Dns01
renewBeforeExpiryDays: 30
सेटिंग्स, डिफ़ॉल्ट मान और बेस्ट प्रैक्टिस
| सेटिंग / स्थान | विवरण | डिफ़ॉल्ट मान | उत्पादन बेस्ट प्रैक्टिस |
|---|---|---|---|
config export [--area a,b] [--split ordner/] [-o datei] |
सर्वर की स्थिति को निर्धारक (deterministic) रूप से निर्यात करता है: निश्चित दस्तावेज़ क्रम, क्रमबद्ध सूचियाँ, कोई टाइमस्टैम्प नहीं। दो बार निर्यात करने पर वही बाइट्स मिलते हैं, Git diff केवल वास्तविक बदलाव दिखाते हैं। | alle Bereiche |
रिपॉज़िटरी के लिए --split का उपयोग करें (प्रति क्षेत्र एक फ़ाइल: domains.yaml, accounts.yaml ...); छोटे पुल रिक्वेस्ट पढ़ने में आसान रहते हैं। |
config plan -f datei|ordner [--prune] |
प्रति ऑब्जेक्ट दिखाता है: + create, ~ update (field: old -> new) और - delete। एग्ज़िट कोड 0 = कोई परिवर्तन नहीं, 2 = परिवर्तन, 1 = त्रुटि। सीक्रेट केवल "(secret changed)" के रूप में दिखते हैं। | ohne Löschen |
इसे पुल रिक्वेस्ट पर चलाएँ और टिप्पणी के रूप में पोस्ट करें (उदाहरण नीचे)। |
config apply -f ... [--prune] [--yes] [--force] [--dry-run] |
पहले सब कुछ जाँचता है (फ़ील्ड, संदर्भ, सीक्रेट, सुरक्षा नियम), फिर एक निश्चित क्रम में बनाता और अपडेट करता है (टेनेंट, डोमेन, खाते, सूचियाँ, नियम, साझाकरण, ट्रांसपोर्ट ...), और उल्टे क्रम में हटाता है। यदि कोई चरण विफल होता है, तो पहले से पूरे हो चुके चरण पूर्ववत कर दिए जाते हैं। हर परिवर्तन ऑडिट लॉग में दर्ज होता है। | fragt nach |
पाइपलाइन में हमेशा --yes दें; अन्यथा टर्मिनल के बिना apply मना कर देता है। --dry-run, plan के बराबर है। |
--prune und Manifest |
हटाना केवल --prune के साथ और केवल उन क्षेत्रों में होता है जिनका नाम Manifest दस्तावेज़ देता है (Manifest के बिना: केवल फ़ाइल में मौजूद प्रकार)। सर्वर सेटिंग्स कभी नहीं हटाई जातीं। | aus |
Export, Manifest लिखता है; यदि आप केवल कुछ भागों का प्रबंधन करते हैं, तो क्षेत्रों को सीमित करें। |
Schutz: letzter Admin, Domain mit Postfächern |
ऐसा प्लान जो कोई सक्रिय सर्वर एडमिन न छोड़े, और ऐसे डोमेन को हटाना जिसमें अभी भी मेलबॉक्स हों, अस्वीकार कर दिए जाते हैं। --force इसे ओवरराइड करता है (तब डोमेन हटाने के बजाय बंद कर दिया जाता है)। अपनी स्वयं की API कुंजी स्वयं को रद्द नहीं कर सकती। | aktiv |
पाइपलाइन में --force को कभी हार्ड-कोड न करें। |
secretRef: {env: NAME} | {file: pfad} |
मान के बजाय एक सीक्रेट का संदर्भ। nextxch-cli इसे रनर पर हल करता है (कोई भी वेरिएबल, कॉन्फ़िगरेशन के सापेक्ष फ़ाइल)। सर्वर स्वयं केवल NEXTXCH_SECRET_* और <data>/secrets/ के अंतर्गत फ़ाइलें पढ़ता है। समान मान को फिर से लागू करना कोई परिवर्तन नहीं माना जाता। | - |
GitHub/GitLab सीक्रेट स्टोर से सीक्रेट को एनवायरनमेंट वेरिएबल के रूप में जॉब को दें। |
Scopes config.read / config.write |
REST: GET /api/v1/config/export, POST /api/v1/config/plan, POST /api/v1/config/apply, GET /api/v1/config/history, GET /api/v1/config/schema (निःशुल्क)। कुंजियाँ: टेनेंट इंटीग्रेशन कुंजियाँ; ये केवल तब तक काम करती हैं जब तक उनका निर्माता एडमिनिस्ट्रेटर है। सर्वर एडमिन की कुंजियाँ पूरा सर्वर देखती हैं, टेनेंट एडमिन की कुंजियाँ केवल वह टेनेंट। export और plan के लिए config.read पर्याप्त है, apply के लिए config.write चाहिए। | - |
दो कुंजियाँ: पुल रिक्वेस्ट में प्लान के लिए config.read, config.write केवल main पर apply जॉब के लिए, संभव हो तो रनर IP तक सीमित। |
Mandanten-Admins |
टेनेंट एडमिन (सत्र या उनकी किसी कुंजी से) केवल अपने टेनेंट के ऑब्जेक्ट एक्सपोर्ट, प्लान और अप्लाई करता है: Domain (केवल बदलाव, बनाना या हटाना नहीं), Account, DistributionList, Share, GalVisibilityRule, ResourceAccessRule और Webhook. सर्वर सेटिंग्स, टेनेंट, पहचान स्रोत और API कुंजियाँ सर्वर एडमिन के पास रहती हैं; ServerAdmin या ComplianceAuditor भूमिका वाले खाते दायरे से बाहर हैं। --prune केवल अपने टेनेंट के भीतर हटाता है, इतिहास केवल अपने अप्लाई दिखाता है। | - |
रीसेलर और विभागों के लिए: हर टेनेंट अपनी कुंजी के साथ अपनी रिपॉज़िटरी रखता है। "tenant" फ़ील्ड छोड़ी जा सकती है; वह अपने टेनेंट से भर दी जाती है। |
secretRef in Listen |
सूची प्रविष्टियाँ भी secretRef के ज़रिए सीक्रेट लेती हैं: MtaFilters hooks[].authHeaderValue और hooks[].hmacSecret, OpsAlerts targets[].secret (webhook लक्ष्य का HMAC)। प्लान प्रविष्टि को उसकी id या नाम से दर्शाता है, उदाहरण के लिए hooks[spamcheck].authHeaderValue; सूची में सादा मान अस्वीकार किया जाता है। | - |
बिना id वाली प्रविष्टियों को पहले apply पर एक id मिलती है; एक्सपोर्ट के बाद id को फ़ाइल में रखें, तब सूची का क्रम बदलने पर भी सीक्रेट सही प्रविष्टि के साथ रहते हैं। |
ACME zur Laufzeit |
TlsSettings.acme (enabled, directoryUrl, contactEmail, challenge Http01/TlsAlpn01/Dns01, renewBeforeExpiryDays, reusePrivateKeyForDane) बिना रीस्टार्ट के लागू होता है: appsettings.json आधार बना रहता है, अंतर <data>/acme-settings.json में रहते हैं और पूरे क्लस्टर पर लागू होते हैं। डायरेक्टरी URL बदलने से अगले ऑर्डर पर नया ACME खाता बनता है। | appsettings.json |
पहले Let's Encrypt की staging डायरेक्टरी से परीक्षण करें, फिर प्रोडक्शन पर स्विच करें। |
Arten (kinds) |
Tenant, Domain, Account, DistributionList, GalVisibilityRule, ResourceAccessRule, Share, SmtpSettings, Route, TransportRules, SpamFilter, MtaFilters, FirewallRule, TlsSettings, IdentitySource, ApiKey, Webhook, Notifications, OpsAlerts, DnsFailover, Pop3, FileStorage, Booking, ArchivePolicy। सभी फ़ील्ड: JSON Schema। | - |
API कुंजियाँ फ़ाइलों से नहीं बनाई जातीं (उनका सीक्रेट केवल एक बार मौजूद होता है); फ़ाइल लेबल और स्कोप बदलती है और --prune से रद्द करती है। रूम और बुकिंग सेटिंग्स हर क्लस्टर नोड पर समान हैं। |