BLD - add GitHub contribution and release automation

This commit is contained in:
DerEchteAlec
2026-08-19 14:20:46 +02:00
parent 086b77e464
commit 05169acbfe
14 changed files with 916 additions and 0 deletions
+114
View File
@@ -0,0 +1,114 @@
name: Bug report
description: Report a reproducible problem in Sky Phone.
title: "[Bug] "
body:
- type: markdown
attributes:
value: |
Thanks for helping improve Sky Phone. Search existing issues first and remove secrets, tokens, player identifiers, and private URLs from all logs.
- type: input
id: version
attributes:
label: Sky Phone version
description: Use the release tag or exact commit SHA. "Latest" is not a version.
placeholder: 0.1.0 or 0123456789abcdef...
validations:
required: true
- type: dropdown
id: framework
attributes:
label: Framework
options:
- ESX Legacy
- QBCore
- Qbox
- Other or custom bridge
validations:
required: true
- type: input
id: artifacts
attributes:
label: FiveM server artifact
description: Provide the exact artifact build number.
placeholder: "12345"
validations:
required: true
- type: input
id: database
attributes:
label: Database
description: Include product and version, for example MariaDB 11.4.
placeholder: MariaDB 11.4
validations:
required: true
- type: textarea
id: integrations
attributes:
label: Relevant integrations
description: List the configured inventory, voice, housing, garage, media, and other providers involved.
placeholder: |
Inventory: ox_inventory 2.x
Voice: pma-voice 1.x
validations:
required: false
- type: textarea
id: description
attributes:
label: Problem description
description: Describe what happens and which player or server state is affected.
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Reproduction steps
description: Provide the smallest reliable sequence, starting from a clean resource restart where possible.
placeholder: |
1. Start ...
2. Open ...
3. Select ...
4. Observe ...
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
validations:
required: true
- type: textarea
id: logs
attributes:
label: Client, server, and NUI evidence
description: Attach relevant F8, server console, NUI console, network, or database output. Include the first error, not only follow-up errors.
validations:
required: true
- type: textarea
id: regression
attributes:
label: Regression information
description: State the last known working tag or commit, if applicable.
validations:
required: false
- type: checkboxes
id: confirmations
attributes:
label: Confirmation
options:
- label: I reproduced this with the resource from this repository and included its exact version.
required: true
- label: I removed secrets, credentials, private URLs, and player-identifying data.
required: true
- label: This is not an exploitable security vulnerability that should be reported privately.
required: true
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/sky-systems/sky_phone/security/advisories/new
about: Report exploitable vulnerabilities privately. Do not open a public issue.
- name: Installation and configuration support
url: https://discord.gg/sky-systems
about: Ask for setup help in the Sky-Systems community.
+35
View File
@@ -0,0 +1,35 @@
name: Documentation improvement
description: Report missing, incorrect, or unclear project documentation.
title: "[Docs] "
body:
- type: input
id: location
attributes:
label: Documentation location
description: Link the section or provide the repository path and heading.
placeholder: README.md, Quick installation
validations:
required: true
- type: textarea
id: problem
attributes:
label: What is unclear or incorrect?
validations:
required: true
- type: textarea
id: correction
attributes:
label: Suggested correction
description: Include verified runtime, configuration, or version context where relevant.
validations:
required: true
- type: checkboxes
id: confirmations
attributes:
label: Confirmation
options:
- label: I checked the current default branch before reporting this.
required: true
@@ -0,0 +1,82 @@
name: Feature request
description: Propose a focused improvement to Sky Phone.
title: "[Feature] "
body:
- type: markdown
attributes:
value: |
Explain the user problem before the proposed implementation. New features must preserve Sky Phone's standalone architecture and server-authoritative state changes.
- type: textarea
id: problem
attributes:
label: Problem or opportunity
description: Who needs this, and what can they not do today?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed behavior
description: Describe the user-visible result and the expected client, server, NUI, or persistence flow.
validations:
required: true
- type: dropdown
id: scope
attributes:
label: Primary area
options:
- Phone app or NUI
- Client behavior
- Server behavior
- Framework or third-party integration
- Configuration or localization
- Database or migration
- Developer API or custom apps
- Build, release, or documentation
validations:
required: true
- type: textarea
id: authority
attributes:
label: Authority and data ownership
description: For state-changing behavior, explain what the server validates and which sky_phone-owned data changes.
validations:
required: false
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Describe existing configuration, integrations, or workflows that were considered.
validations:
required: false
- type: textarea
id: compatibility
attributes:
label: Compatibility impact
description: Note affected frameworks, providers, configs, locales, schemas, exports, or public events.
validations:
required: false
- type: textarea
id: references
attributes:
label: References or mockups
description: Attach concise examples, diagrams, or screenshots when they materially clarify the request.
validations:
required: false
- type: checkboxes
id: confirmations
attributes:
label: Confirmation
options:
- label: This proposal does not require sky_base, sky_jobs_base, or another Sky resource.
required: true
- label: I searched for an existing issue covering the same request.
required: true
+46
View File
@@ -0,0 +1,46 @@
## Summary
<!-- What user or developer problem does this PR solve? -->
Closes #
## Root cause and approach
<!-- For fixes: trace the real failing path. For features: explain the chosen boundary and behavior. -->
## Changes
-
## Compatibility and migrations
<!-- List config, locale, SQL, framework, public API, event, export, or deployment impact. Write "None" when not applicable. -->
## Validation
- [ ] I ran the narrowest relevant automated tests.
- [ ] I ran `pnpm typecheck`, `pnpm lint`, and `pnpm test` for frontend changes.
- [ ] I ran a production frontend build for frontend changes.
- [ ] I tested affected Lua/native behavior with experimental OAL enabled, or marked the live runtime test as pending below.
- [ ] I verified every changed NUI callback responds on every reachable path.
- [ ] I reviewed the final diff and excluded unrelated work and generated-only edits.
Commands and results:
```text
```
## Runtime evidence
<!-- Distinguish local/static/build checks from an actual restarted FiveM runtime test. Add screenshots for visible UI changes. -->
## Security and architecture
- [ ] Consequential actions remain server-authoritative and validate identity, permissions, ownership, limits, and payloads.
- [ ] This change introduces no dependency, event, export, global, config, persistence, or fallback connection to another `sky_*` resource.
- [ ] No secret, credential, private URL, or personal player data is included.
## Reviewer notes
<!-- Call out risky areas, intentional tradeoffs, or follow-up work. -->
+20
View File
@@ -0,0 +1,20 @@
# Repository rulesets
These JSON files are import-ready repository rulesets for `sky-systems/sky_phone`.
## Activation order
1. Merge the governance files and workflows into the default `dev` branch.
2. Let `CI` and `Pull request policy` run once so GitHub registers the check names.
3. Open **Settings > Rules > Rulesets > New ruleset > Import a ruleset**.
4. Import `protect-dev.json`, review its target and required checks, then activate it.
5. Import `protect-release-tags.json`, review the numeric tag pattern, then activate it.
6. Open a test pull request and confirm all three required checks are reported before relying on the ruleset.
`protect-dev.json` blocks deletion and force pushes, requires one approval, dismisses stale reviews, requires approval after the last push, resolves review conversations, and requires the `Repository policy`, `Frontend`, and `Pull request policy` checks.
`protect-release-tags.json` accepts stable numeric semantic versions such as `0.2.0`, rejects a leading `v`, and makes created tags immutable.
The built-in repository `Maintain` role (`RepositoryRole` actor ID `2`) has an explicit emergency bypass in both files. Use it only for audited recovery work. Remove the bypass in GitHub if maintainers should remain subject to every rule; changing the committed JSON alone does not update an already imported ruleset.
Rulesets are GitHub settings, not live configuration files. Committing or editing these JSON files does not activate or update protection automatically; an administrator must import or reconcile them in GitHub.
+56
View File
@@ -0,0 +1,56 @@
{
"name": "Protect the default development branch",
"target": "branch",
"source_type": "Repository",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["~DEFAULT_BRANCH"],
"exclude": []
}
},
"rules": [
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "pull_request",
"parameters": {
"allowed_merge_methods": ["merge", "squash", "rebase"],
"dismiss_stale_reviews_on_push": true,
"require_code_owner_review": false,
"require_last_push_approval": true,
"required_approving_review_count": 1,
"required_review_thread_resolution": true
}
},
{
"type": "required_status_checks",
"parameters": {
"do_not_enforce_on_create": true,
"required_status_checks": [
{
"context": "Repository policy"
},
{
"context": "Frontend"
},
{
"context": "Pull request policy"
}
],
"strict_required_status_checks_policy": true
}
}
],
"bypass_actors": [
{
"actor_id": 2,
"actor_type": "RepositoryRole",
"bypass_mode": "always"
}
]
}
@@ -0,0 +1,36 @@
{
"name": "Protect semantic release tags",
"target": "tag",
"source_type": "Repository",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["~ALL"],
"exclude": []
}
},
"rules": [
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "tag_name_pattern",
"parameters": {
"name": "Numeric semantic version without v prefix",
"negate": false,
"operator": "regex",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+\\n?$"
}
}
],
"bypass_actors": [
{
"actor_id": 2,
"actor_type": "RepositoryRole",
"bypass_mode": "always"
}
]
}
+150
View File
@@ -0,0 +1,150 @@
import { readFile, readdir } from "node:fs/promises";
import { dirname, extname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const scriptDirectory = dirname(fileURLToPath(import.meta.url));
const repositoryRoot = resolve(scriptDirectory, "..", "..");
const failures = [];
function fail(message) {
failures.push(message);
}
const manifestPath = join(repositoryRoot, "sky_phone", "fxmanifest.lua");
const manifest = await readFile(manifestPath, "utf8");
for (const requiredFragment of [
"fx_version 'cerulean'",
"node_version '22'",
"use_experimental_fxv2_oal 'yes'",
"ui_page 'source/html/index.html'",
]) {
if (!manifest.includes(requiredFragment)) {
fail(`fxmanifest.lua is missing required contract: ${requiredFragment}`);
}
}
const rulesetDirectory = join(repositoryRoot, ".github", "rulesets");
const rulesetFiles = (await readdir(rulesetDirectory)).filter((file) =>
file.endsWith(".json"),
);
const requiredContexts = new Set([
"Repository policy",
"Frontend",
"Pull request policy",
]);
for (const file of rulesetFiles) {
const path = join(rulesetDirectory, file);
let ruleset;
try {
ruleset = JSON.parse(await readFile(path, "utf8"));
} catch (error) {
fail(`${file} is not valid JSON: ${error.message}`);
continue;
}
if (!ruleset.name || !["branch", "tag"].includes(ruleset.target)) {
fail(`${file} must define a name and a branch or tag target`);
}
if (!Array.isArray(ruleset.rules) || ruleset.rules.length === 0) {
fail(`${file} must contain at least one rule`);
}
const maintainBypass = ruleset.bypass_actors?.some(
(actor) =>
actor.actor_type === "RepositoryRole" &&
actor.actor_id === 2 &&
actor.bypass_mode === "always",
);
if (!maintainBypass) {
fail(`${file} must retain the Maintain role emergency bypass`);
}
const statusRule = ruleset.rules?.find(
(rule) => rule.type === "required_status_checks",
);
if (statusRule) {
const contexts = new Set(
statusRule.parameters?.required_status_checks?.map(
(check) => check.context,
) ?? [],
);
for (const context of requiredContexts) {
if (!contexts.has(context)) {
fail(`${file} is missing required status context: ${context}`);
}
}
}
}
const sourceRoots = [
join(repositoryRoot, "sky_phone"),
join(repositoryRoot, "frontend", "src"),
];
const inspectedExtensions = new Set([
".cjs",
".js",
".json",
".lua",
".mjs",
".sql",
".ts",
".vue",
]);
const forbiddenPatterns = [
{
label: "a forbidden Sky resource reference",
pattern: /\bsky_(?:base|jobs_base)(?::|\b)/i,
},
{
label: "a forbidden shared Sky global",
pattern: /\bSky\.(?:FW|Cb|DB|Query)\b/,
},
];
async function inspectDirectory(directory) {
for (const entry of await readdir(directory, { withFileTypes: true })) {
if (
entry.name === "html" &&
directory.endsWith(join("sky_phone", "source"))
) {
continue;
}
const path = join(directory, entry.name);
if (entry.isDirectory()) {
await inspectDirectory(path);
continue;
}
if (!inspectedExtensions.has(extname(entry.name))) {
continue;
}
const content = await readFile(path, "utf8");
for (const { label, pattern } of forbiddenPatterns) {
if (pattern.test(content)) {
fail(`${path.slice(repositoryRoot.length + 1)} contains ${label}`);
}
}
}
}
for (const sourceRoot of sourceRoots) {
await inspectDirectory(sourceRoot);
}
if (failures.length > 0) {
console.error("Repository policy validation failed:");
for (const failure of failures) {
console.error(`- ${failure}`);
}
process.exit(1);
}
console.log(
`Repository policy validation passed (${rulesetFiles.length} rulesets checked).`,
);
+94
View File
@@ -0,0 +1,94 @@
name: CI
on:
pull_request:
push:
branches:
- dev
workflow_dispatch:
concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
repository-policy:
name: Repository policy
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version: 22
- name: Validate repository contracts
run: node .github/scripts/validate-repository.mjs
- name: Install Lua compiler
run: |
sudo apt-get update
sudo apt-get install --yes lua5.4
- name: Check Lua syntax
shell: bash
run: |
set -euo pipefail
while IFS= read -r -d '' file; do
luac5.4 -p "$file"
done < <(find sky_phone tests -type f -name '*.lua' -print0)
frontend:
name: Frontend
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: frontend
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up pnpm
uses: pnpm/action-setup@v6
with:
version: 10.33.0
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm
cache-dependency-path: frontend/pnpm-lock.yaml
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Check repository formatting and schemas
run: >-
pnpm exec prettier --check
"../.github/**/*.{yml,yaml,json,md}"
"../.github/scripts/*.mjs"
"../CONTRIBUTING.md"
"../SECURITY.md"
- name: Lint frontend
run: pnpm lint
- name: Typecheck frontend
run: pnpm typecheck
- name: Test frontend
run: pnpm test
- name: Build deployable NUI
run: pnpm build-only
- name: Verify published NUI entrypoint
run: test -f ../sky_phone/source/html/index.html
+61
View File
@@ -0,0 +1,61 @@
name: Pull request policy
on:
pull_request:
types:
- opened
- edited
- reopened
- synchronize
- ready_for_review
permissions:
contents: read
jobs:
policy:
name: Pull request policy
runs-on: ubuntu-latest
timeout-minutes: 5
env:
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
steps:
- name: Validate target branch
shell: bash
run: |
set -euo pipefail
if [[ "$BASE_REF" != "dev" ]]; then
echo "Pull requests must target dev; received: $BASE_REF"
exit 1
fi
- name: Validate source branch
shell: bash
run: |
set -euo pipefail
branch_pattern='^(feat|feature|fix|hotfix|docs|refactor|perf|test|build|ci|chore|release)/[a-z0-9]+([._-][a-z0-9]+)*$'
if [[ "$HEAD_REF" == dependabot/* ]]; then
exit 0
fi
if [[ ! "$HEAD_REF" =~ $branch_pattern ]]; then
echo "Invalid branch name: $HEAD_REF"
echo "Expected type/lowercase-kebab-case; see CONTRIBUTING.md."
exit 1
fi
- name: Validate pull request title
shell: bash
run: |
set -euo pipefail
if [[ "$PR_AUTHOR" == "dependabot[bot]" ]]; then
exit 0
fi
title_pattern='^(ENH|ADD|FIX|DOC|BLD|PERF|CLN|TRY) - [^[:space:]].{4,72}$'
if [[ ! "$PR_TITLE" =~ $title_pattern ]] || (( ${#PR_TITLE} > 80 )); then
echo "Invalid pull request title: $PR_TITLE"
echo "Expected: TAG - short imperative summary (maximum 80 characters)."
exit 1
fi
+115
View File
@@ -0,0 +1,115 @@
name: Release
on:
push:
tags:
- "*.*.*"
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write
jobs:
release:
name: Build and publish release
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out tagged source
uses: actions/checkout@v6
- name: Validate tag and manifest version
shell: bash
run: |
set -euo pipefail
if [[ ! "$GITHUB_REF_NAME" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Release tags must use numeric semantic versions without v: $GITHUB_REF_NAME"
exit 1
fi
manifest_version=$(sed -n "s/^version '\([^']*\)'/\1/p" sky_phone/fxmanifest.lua)
if [[ "$manifest_version" != "$GITHUB_REF_NAME" ]]; then
echo "Tag $GITHUB_REF_NAME does not match fxmanifest version $manifest_version"
exit 1
fi
- name: Set up pnpm
uses: pnpm/action-setup@v6
with:
version: 10.33.0
- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm
cache-dependency-path: frontend/pnpm-lock.yaml
- name: Install frontend dependencies
working-directory: frontend
run: pnpm install --frozen-lockfile
- name: Validate, test, and build frontend
working-directory: frontend
run: |
pnpm lint
pnpm test
pnpm build
- name: Verify deployable phone resource
shell: bash
run: |
set -euo pipefail
test -f sky_phone/fxmanifest.lua
test -f sky_phone/source/html/index.html
test -d sky_phone/source/html/assets
- name: Validate repository contracts
run: node .github/scripts/validate-repository.mjs
- name: Install Lua compiler
run: |
sudo apt-get update
sudo apt-get install --yes lua5.4
- name: Check Lua syntax
shell: bash
run: |
set -euo pipefail
while IFS= read -r -d '' file; do
luac5.4 -p "$file"
done < <(find sky_phone tests -type f -name '*.lua' -print0)
- name: Create release archive
shell: bash
run: |
set -euo pipefail
archive="sky_phone-${GITHUB_REF_NAME}.zip"
zip -r "$archive" sky_phone
unzip -t "$archive"
unzip -Z1 "$archive" | grep -Fx "sky_phone/fxmanifest.lua"
unzip -Z1 "$archive" | grep -Fx "sky_phone/source/html/index.html"
sha256sum "$archive" > "${archive}.sha256"
- name: Upload workflow artifact
uses: actions/upload-artifact@v7
with:
name: sky_phone-${{ github.ref_name }}
path: |
sky_phone-${{ github.ref_name }}.zip
sky_phone-${{ github.ref_name }}.zip.sha256
if-no-files-found: error
retention-days: 30
- name: Publish GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: >-
gh release create "$GITHUB_REF_NAME"
"sky_phone-${GITHUB_REF_NAME}.zip"
"sky_phone-${GITHUB_REF_NAME}.zip.sha256"
--verify-tag
--generate-notes
--title "Sky Phone $GITHUB_REF_NAME"
+84
View File
@@ -0,0 +1,84 @@
# Contributing to Sky Phone
Sky Phone uses `dev` as its default integration branch. All normal changes reach `dev` through a pull request; do not push feature work directly to it.
## Branches
Create a short-lived branch from an up-to-date `dev` branch. Use lowercase kebab-case after one of these prefixes:
| Prefix | Purpose |
| ----------- | ------------------------------------------------ |
| `feat/` | New behavior or app capability |
| `fix/` | Bug fix |
| `hotfix/` | Urgent release repair |
| `docs/` | Documentation only |
| `refactor/` | Internal change without intended behavior change |
| `perf/` | Performance work |
| `test/` | Test-only work |
| `build/` | Build or packaging work |
| `ci/` | GitHub Actions and repository automation |
| `chore/` | Focused maintenance |
| `release/` | Release preparation |
`feature/` remains accepted for existing branches, but new feature branches should use `feat/`.
Examples: `feat/mail-signatures`, `fix/radio-focus`, `ci/release-package`. Avoid personal names, issue titles, uppercase characters, spaces, and branches that combine unrelated work.
## Commits and pull requests
Use the repository commit format:
```text
TAG - short imperative summary
```
Allowed tags are `ENH`, `ADD`, `FIX`, `DOC`, `BLD`, `PERF`, `CLN`, and `TRY`. Examples:
```text
FIX - validate mail ownership before deletion
ENH - add per-account notification settings
DOC - clarify Qbox installation order
```
Use the same format for the pull request title. Keep commits focused, stage only task files, link the issue, and complete the pull request template with actual commands and results.
## Architecture and security rules
- Sky Phone is standalone. It must not depend on or exchange state with `sky_base`, `sky_jobs_base`, or another `sky_*` resource.
- The server validates and decides permissions, identity, money, inventory, ownership, proximity, limits, and all other consequential state.
- Treat NUI and client payloads as untrusted. Every NUI callback must invoke its response callback on every reachable path.
- Parameterize SQL, keep persistence owned by `sky_phone`, and send only the required data over the network.
- Keep credentials, tokens, private endpoints, and player-identifying data out of source, fixtures, logs, issues, and pull requests.
## Schema, configuration, and compatibility
- Prefix new resource-owned tables, persistent keys, convars, callbacks, and events with `sky_phone` where the technology permits it.
- Keep the runtime schema in `sky_phone/source/server/db_migrate.lua` and the clean-install schema in `sky_phone/sql/install.sql` aligned.
- Prefer additive, idempotent migrations. Destructive or lossy migrations require an explicit migration plan, backup guidance, and reviewer approval.
- Do not force a database charset or collation unless a documented compatibility requirement has been reviewed.
- Preserve public events, callbacks, exports, configuration defaults, and stored data unless the linked issue explicitly authorizes a breaking change.
- Update both English and German locales for user-facing text. Logs and developer diagnostics remain in English.
- New or substantially changed NUI screens use the public Sky UI components and semantic tokens under `frontend/src/ui`.
## Local validation
Install frontend dependencies with pnpm, then run the checks relevant to the change:
```powershell
cd frontend
pnpm install --frozen-lockfile
pnpm typecheck
pnpm lint
pnpm test
pnpm build
```
The build publishes the generated NUI into `sky_phone/source/html`. Do not hand-edit generated output. A successful build proves source/build consistency, not behavior inside FiveM; report live runtime testing separately.
Lua, config, manifest, locale, SQL, and native changes must also be tested in a restarted FiveM resource with experimental OAL enabled. Pass native coordinates as separate numeric arguments and verify native signatures against authoritative documentation.
## Review and merge
A pull request is ready when required checks pass, review conversations are resolved, the latest push is approved by someone other than its author, and migrations or operational steps are explicit. The default ruleset allows merge, squash, and rebase so maintainers can preserve meaningful merge history when needed.
Release tags use numeric semantic versions without a `v` prefix, for example `0.2.0`. Tags are immutable after creation.
+15
View File
@@ -0,0 +1,15 @@
# Security policy
## Reporting a vulnerability
Do not open a public issue for an exploitable vulnerability or include exploit details in Discord, logs, screenshots, or pull requests.
Use [GitHub private vulnerability reporting](https://github.com/sky-systems/sky_phone/security/advisories/new). If that form is unavailable, use the private contact listed on the [official Sky-Systems contact page](https://www.sky-systems.net/impressum).
Include the affected release tag or commit, framework and integration context, impact, minimal reproduction, and any proposed mitigation. Remove credentials, tokens, private server addresses, and player-identifying data.
We will acknowledge the report, reproduce and assess the impact, coordinate a fix, and publish details after affected users have a reasonable update path. Please do not disclose the issue publicly before that coordination is complete.
## Supported versions
Security fixes target the latest published release and the current `dev` branch. Older releases may require upgrading before a fix can be applied.