fognode 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. fognode-0.1.0/.github/CODEOWNERS +1 -0
  2. fognode-0.1.0/.github/PULL_REQUEST_TEMPLATE/bug_fix.md +12 -0
  3. fognode-0.1.0/.github/PULL_REQUEST_TEMPLATE/optimization.md +12 -0
  4. fognode-0.1.0/.github/dependabot.yml +11 -0
  5. fognode-0.1.0/.github/workflows/build.yml +129 -0
  6. fognode-0.1.0/.github/workflows/pr.yml +155 -0
  7. fognode-0.1.0/.gitignore +31 -0
  8. fognode-0.1.0/CHANGELOG.md +12 -0
  9. fognode-0.1.0/CONTRIBUTING.md +30 -0
  10. fognode-0.1.0/LICENSE +21 -0
  11. fognode-0.1.0/PKG-INFO +150 -0
  12. fognode-0.1.0/README.md +112 -0
  13. fognode-0.1.0/examples/headless_server.py +14 -0
  14. fognode-0.1.0/pyproject.toml +80 -0
  15. fognode-0.1.0/src/fognode/__init__.py +120 -0
  16. fognode-0.1.0/src/fognode/__main__.py +5 -0
  17. fognode-0.1.0/src/fognode/app.py +270 -0
  18. fognode-0.1.0/src/fognode/auth/__init__.py +5 -0
  19. fognode-0.1.0/src/fognode/auth/handshake.py +107 -0
  20. fognode-0.1.0/src/fognode/ciphers/__init__.py +57 -0
  21. fognode-0.1.0/src/fognode/ciphers/aesgcm.py +5 -0
  22. fognode-0.1.0/src/fognode/ciphers/blake2.py +14 -0
  23. fognode-0.1.0/src/fognode/ciphers/blowfish.py +32 -0
  24. fognode-0.1.0/src/fognode/ciphers/chacha20.py +23 -0
  25. fognode-0.1.0/src/fognode/ciphers/ed25519.py +23 -0
  26. fognode-0.1.0/src/fognode/ciphers/fernet.py +23 -0
  27. fognode-0.1.0/src/fognode/ciphers/hkdf.py +5 -0
  28. fognode-0.1.0/src/fognode/ciphers/hmac.py +5 -0
  29. fognode-0.1.0/src/fognode/ciphers/pbkdf2.py +5 -0
  30. fognode-0.1.0/src/fognode/ciphers/rsa.py +61 -0
  31. fognode-0.1.0/src/fognode/ciphers/scrypt.py +15 -0
  32. fognode-0.1.0/src/fognode/ciphers/sha3.py +18 -0
  33. fognode-0.1.0/src/fognode/ciphers/x25519.py +5 -0
  34. fognode-0.1.0/src/fognode/cli/__init__.py +5 -0
  35. fognode-0.1.0/src/fognode/cli/entrypoint.py +398 -0
  36. fognode-0.1.0/src/fognode/core/__init__.py +8 -0
  37. fognode-0.1.0/src/fognode/core/client.py +54 -0
  38. fognode-0.1.0/src/fognode/core/probe.py +42 -0
  39. fognode-0.1.0/src/fognode/core/server.py +182 -0
  40. fognode-0.1.0/src/fognode/core/state.py +66 -0
  41. fognode-0.1.0/src/fognode/crypto/__init__.py +43 -0
  42. fognode-0.1.0/src/fognode/crypto/cert.py +159 -0
  43. fognode-0.1.0/src/fognode/crypto/channel.py +82 -0
  44. fognode-0.1.0/src/fognode/crypto/kdf.py +7 -0
  45. fognode-0.1.0/src/fognode/crypto/kx.py +15 -0
  46. fognode-0.1.0/src/fognode/crypto/password.py +21 -0
  47. fognode-0.1.0/src/fognode/crypto/primitives.py +63 -0
  48. fognode-0.1.0/src/fognode/decorators.py +66 -0
  49. fognode-0.1.0/src/fognode/exceptions.py +12 -0
  50. fognode-0.1.0/src/fognode/filters/__init__.py +7 -0
  51. fognode-0.1.0/src/fognode/filters/base.py +8 -0
  52. fognode-0.1.0/src/fognode/filters/command.py +15 -0
  53. fognode-0.1.0/src/fognode/filters/text.py +11 -0
  54. fognode-0.1.0/src/fognode/handlers/__init__.py +5 -0
  55. fognode-0.1.0/src/fognode/handlers/handler.py +36 -0
  56. fognode-0.1.0/src/fognode/types/__init__.py +105 -0
  57. fognode-0.1.0/src/fognode/types/constants.py +25 -0
  58. fognode-0.1.0/src/fognode/types/exceptions.py +25 -0
  59. fognode-0.1.0/src/fognode/types/protocol.py +128 -0
  60. fognode-0.1.0/src/fognode/utils/__init__.py +7 -0
  61. fognode-0.1.0/src/fognode/utils/ipwords.py +286 -0
  62. fognode-0.1.0/src/fognode/utils/net.py +12 -0
  63. fognode-0.1.0/src/fognode/utils/ratelimit.py +43 -0
  64. fognode-0.1.0/src/fognode/wire/__init__.py +5 -0
  65. fognode-0.1.0/src/fognode/wire/framing.py +33 -0
  66. fognode-0.1.0/tests/__init__.py +0 -0
  67. fognode-0.1.0/tests/conftest.py +8 -0
  68. fognode-0.1.0/tests/test_crypto.py +52 -0
  69. fognode-0.1.0/tests/test_framing.py +40 -0
  70. fognode-0.1.0/tests/test_ipwords.py +20 -0
  71. fognode-0.1.0/tests/test_ratelimit.py +25 -0
@@ -0,0 +1 @@
1
+ * @IMDelewer
@@ -0,0 +1,12 @@
1
+ ## Bug fix
2
+
3
+ - [ ] `ruff check src/ tests/` — no errors
4
+ - [ ] `black --check src/ tests/` — no changes needed
5
+ - [ ] `pyright src/` — no errors
6
+ - [ ] `pytest tests/` — all pass
7
+
8
+ ### What bug is fixed?
9
+
10
+ ### Root cause
11
+
12
+ ### How was it fixed?
@@ -0,0 +1,12 @@
1
+ ## Optimization
2
+
3
+ - [ ] `ruff check src/ tests/` — no errors
4
+ - [ ] `black --check src/ tests/` — no changes needed
5
+ - [ ] `pyright src/` — no errors
6
+ - [ ] `pytest tests/` — all pass
7
+
8
+ ### What was optimized?
9
+
10
+ ### Measured improvement
11
+
12
+ ### Risks / trade-offs
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+ target-branch: dev
8
+ labels:
9
+ - dependencies
10
+ reviewers:
11
+ - IMDelewer
@@ -0,0 +1,129 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ version:
7
+ description: "Release version"
8
+ required: true
9
+ default: "v0.1.0"
10
+
11
+ publish_pypi:
12
+ description: "Publish package to PyPI"
13
+ type: boolean
14
+ required: true
15
+ default: true
16
+
17
+ permissions:
18
+ contents: write
19
+ id-token: write
20
+
21
+ jobs:
22
+ release:
23
+ name: Release
24
+ runs-on: ubuntu-latest
25
+
26
+ environment:
27
+ name: pypi
28
+
29
+ steps:
30
+ - name: Checkout
31
+ uses: actions/checkout@v4
32
+ with:
33
+ fetch-depth: 0
34
+
35
+ - name: Setup Python
36
+ uses: actions/setup-python@v5
37
+ with:
38
+ python-version: "3.12"
39
+ cache: pip
40
+
41
+ - name: Validate version format
42
+ run: |
43
+ VERSION="${{ github.event.inputs.version }}"
44
+ if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
45
+ echo "Invalid version format: $VERSION"
46
+ echo "Expected: vX.Y.Z"
47
+ exit 1
48
+ fi
49
+ echo "Version format valid: $VERSION"
50
+
51
+ - name: Install build dependencies
52
+ run: |
53
+ python -m pip install --upgrade pip
54
+ pip install build twine hatchling
55
+
56
+ - name: Build package
57
+ run: python -m build
58
+
59
+ - name: Verify package
60
+ run: twine check dist/*
61
+
62
+ - name: Generate release notes
63
+ id: notes
64
+ run: |
65
+ PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
66
+
67
+ if [ -z "$PREV_TAG" ]; then
68
+ LOG=$(git log --max-count=50 --pretty=format:"- %s")
69
+ else
70
+ LOG=$(git log ${PREV_TAG}..HEAD --max-count=100 --pretty=format:"- %s")
71
+ fi
72
+
73
+ {
74
+ echo 'notes<<EOF'
75
+ echo "# fognode ${{ github.event.inputs.version }}"
76
+ echo
77
+ echo "## Changes"
78
+ echo
79
+ echo "$LOG"
80
+ echo
81
+ echo "## Installation"
82
+ echo
83
+ echo '```bash'
84
+ echo 'pip install fognode'
85
+ echo '```'
86
+ echo
87
+ echo "## PyPI"
88
+ echo
89
+ echo "https://pypi.org/project/fognode/"
90
+ echo
91
+ echo 'EOF'
92
+ } >> "$GITHUB_OUTPUT"
93
+
94
+ - name: Check if tag exists
95
+ id: check_tag
96
+ run: |
97
+ TAG="${{ github.event.inputs.version }}"
98
+ if git rev-parse "$TAG" >/dev/null 2>&1; then
99
+ echo "Tag $TAG already exists"
100
+ echo "exists=true" >> "$GITHUB_OUTPUT"
101
+ else
102
+ echo "exists=false" >> "$GITHUB_OUTPUT"
103
+ fi
104
+
105
+ - name: Create tag
106
+ if: steps.check_tag.outputs.exists == 'false'
107
+ run: |
108
+ TAG="${{ github.event.inputs.version }}"
109
+ git tag "$TAG"
110
+ git push origin "$TAG"
111
+
112
+ - name: Create GitHub release
113
+ if: steps.check_tag.outputs.exists == 'false'
114
+ uses: softprops/action-gh-release@v2
115
+ with:
116
+ tag_name: ${{ github.event.inputs.version }}
117
+ name: fognode ${{ github.event.inputs.version }}
118
+ body: ${{ steps.notes.outputs.notes }}
119
+ generate_release_notes: false
120
+ files: |
121
+ dist/*
122
+
123
+ - name: Publish to PyPI
124
+ if: github.event.inputs.publish_pypi == 'true'
125
+ uses: pypa/gh-action-pypi-publish@release/v1
126
+ with:
127
+ attestations: false
128
+ skip-existing: true
129
+ verbose: true
@@ -0,0 +1,155 @@
1
+ name: PR Checks
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main, dev]
6
+
7
+ jobs:
8
+ lint:
9
+ name: Lint & Types
10
+ runs-on: ubuntu-latest
11
+ outputs:
12
+ ruff: ${{ steps.ruff.outcome }}
13
+ black: ${{ steps.black.outcome }}
14
+ pyright: ${{ steps.pyright.outcome }}
15
+
16
+ steps:
17
+ - name: Checkout
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Set up Python 3.10
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.10"
24
+ cache: pip
25
+
26
+ - name: Install dependencies
27
+ run: pip install -e ".[dev]"
28
+
29
+ - name: Ruff
30
+ id: ruff
31
+ continue-on-error: true
32
+ run: ruff check src/ tests/
33
+
34
+ - name: Black
35
+ id: black
36
+ continue-on-error: true
37
+ run: black --check src/ tests/
38
+
39
+ - name: Pyright
40
+ id: pyright
41
+ continue-on-error: true
42
+ run: pyright src/
43
+
44
+ test:
45
+ name: Python ${{ matrix.python }}
46
+ runs-on: ubuntu-latest
47
+ strategy:
48
+ fail-fast: false
49
+ matrix:
50
+ python: ["3.10", "3.12", "3.14"]
51
+
52
+ steps:
53
+ - name: Checkout
54
+ uses: actions/checkout@v4
55
+
56
+ - name: Set up Python ${{ matrix.python }}
57
+ uses: actions/setup-python@v5
58
+ with:
59
+ python-version: ${{ matrix.python }}
60
+ allow-prereleases: true
61
+ cache: pip
62
+
63
+ - name: Install dependencies
64
+ run: pip install -e ".[dev]"
65
+
66
+ - name: Pytest
67
+ id: pytest
68
+ continue-on-error: true
69
+ run: pytest tests/ -v --tb=short
70
+
71
+ - name: Save result
72
+ if: always()
73
+ run: echo "${{ steps.pytest.outcome }}" > pytest-result.txt
74
+
75
+ - name: Upload result
76
+ if: always()
77
+ uses: actions/upload-artifact@v4
78
+ with:
79
+ name: result-py${{ matrix.python }}
80
+ path: pytest-result.txt
81
+
82
+ comment:
83
+ name: PR Comment
84
+ runs-on: ubuntu-latest
85
+ needs: [lint, test]
86
+ if: always()
87
+ permissions:
88
+ pull-requests: write
89
+
90
+ steps:
91
+ - name: Download results
92
+ uses: actions/download-artifact@v4
93
+ with:
94
+ path: results
95
+
96
+ - name: Build comment
97
+ id: build
98
+ run: |
99
+ ruff="${{ needs.lint.outputs.ruff }}"
100
+ black="${{ needs.lint.outputs.black }}"
101
+ pyright="${{ needs.lint.outputs.pyright }}"
102
+
103
+ icon() { [ "$1" = "success" ] && echo "✅" || echo "❌"; }
104
+ label() { [ "$1" = "success" ] && echo "Passed" || echo "Failed"; }
105
+
106
+ py310=$(cat results/result-py3.10/pytest-result.txt 2>/dev/null || echo "unknown")
107
+ py312=$(cat results/result-py3.12/pytest-result.txt 2>/dev/null || echo "unknown")
108
+ py314=$(cat results/result-py3.14/pytest-result.txt 2>/dev/null || echo "unknown")
109
+
110
+ all_passed="true"
111
+ for v in "$ruff" "$black" "$pyright" "$py310" "$py312" "$py314"; do
112
+ [ "$v" != "success" ] && all_passed="false" && break
113
+ done
114
+
115
+ [ "$all_passed" = "true" ] \
116
+ && header="✅ PR Check Results" \
117
+ || header="❌ PR Check Results"
118
+
119
+ [ "$all_passed" = "true" ] \
120
+ && footer="All checks passed." \
121
+ || footer="One or more checks failed. Fix the issues and push again."
122
+
123
+ cat > comment.md << EOF
124
+ ## $header
125
+
126
+ ### Lint & Type Checks
127
+
128
+ | Check | Status |
129
+ |-------|--------|
130
+ | Ruff | $(icon "$ruff") $(label "$ruff") |
131
+ | Black | $(icon "$black") $(label "$black") |
132
+ | Pyright | $(icon "$pyright") $(label "$pyright") |
133
+
134
+ ### Tests
135
+
136
+ | Python | Status |
137
+ |--------|--------|
138
+ | 3.10 | $(icon "$py310") $(label "$py310") |
139
+ | 3.12 | $(icon "$py312") $(label "$py312") |
140
+ | 3.14 | $(icon "$py314") $(label "$py314") |
141
+
142
+ > $footer
143
+ EOF
144
+
145
+ {
146
+ echo 'body<<COMMENT_EOF'
147
+ cat comment.md
148
+ echo 'COMMENT_EOF'
149
+ } >> "$GITHUB_OUTPUT"
150
+
151
+ - name: Comment Pull Request
152
+ uses: thollander/actions-comment-pull-request@v3.0.1
153
+ with:
154
+ message: ${{ steps.build.outputs.body }}
155
+ comment-tag: pr-checks
@@ -0,0 +1,31 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ .Python
6
+ build/
7
+ develop-eggs/
8
+ dist/
9
+ downloads/
10
+ eggs/
11
+ .eggs/
12
+ lib/
13
+ lib64/
14
+ parts/
15
+ sdist/
16
+ var/
17
+ wheels/
18
+ *.egg-info/
19
+ .installed.cfg
20
+ *.egg
21
+
22
+ .venv/
23
+ venv/
24
+
25
+ .mypy_cache/
26
+ .pytest_cache/
27
+ .ruff_cache/
28
+
29
+ *.pem
30
+ *.key
31
+ *.bin
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial release.
6
+ - TLS 1.2+ with strong cipher suite.
7
+ - X25519 ephemeral key exchange over TLS.
8
+ - AESGCM-256 + HMAC-SHA256 frame encryption.
9
+ - PBKDF2-HMAC-SHA256 password derivation.
10
+ - Monotonic counter + timestamp replay protection.
11
+ - IP-to-word encoding for human-readable addresses.
12
+ - Rate-limited authentication.
@@ -0,0 +1,30 @@
1
+ # Contributing to fognode
2
+
3
+ Personal library. PRs accepted only in narrow scope.
4
+
5
+ ## Accepted
6
+
7
+ | Type | Accepted |
8
+ |---|---|
9
+ | Bug fixes | yes |
10
+ | Code optimization | yes |
11
+ | Cross-platform improvements | yes |
12
+ | Security hardening | yes |
13
+ | Features | no |
14
+ | Documentation | no |
15
+ | Questions | no |
16
+
17
+ ## Requirements
18
+
19
+ - Python 3.10+
20
+ - `ruff check src/ tests/`
21
+ - `black --check src/ tests/`
22
+ - `pyright src/`
23
+ - `pytest tests/`
24
+ - No comments explaining what code does
25
+ - `from __future__ import annotations` where needed
26
+
27
+ ## PR checklist
28
+
29
+ - [ ] All checks pass
30
+ - [ ] No breaking API changes in `src/fognode/__init__.py`
fognode-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 reekeer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
fognode-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,150 @@
1
+ Metadata-Version: 2.4
2
+ Name: fognode
3
+ Version: 0.1.0
4
+ Summary: fognode - headless secure encrypted data transmission. TLS + X25519 + AESGCM + PBKDF2.
5
+ Project-URL: Repository, https://github.com/reekeer/fognode
6
+ Author: reekeer
7
+ License: MIT License
8
+
9
+ Copyright (c) 2026 reekeer
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+ License-File: LICENSE
29
+ Requires-Python: >=3.10
30
+ Requires-Dist: cryptography>=44.0.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: black>=24.0.0; extra == 'dev'
33
+ Requires-Dist: pyright>=1.1.409; extra == 'dev'
34
+ Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
35
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
36
+ Requires-Dist: ruff>=0.5.0; extra == 'dev'
37
+ Description-Content-Type: text/markdown
38
+
39
+ <h1 align="center">fognode</h1>
40
+
41
+ <h4 align="center">headless secure encrypted data transmission.</h4>
42
+
43
+ <p align="center">
44
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge&logo=opensourceinitiative&logoColor=FFFFFF" alt="License"></a>
45
+ <img src="https://img.shields.io/badge/Python-3.10%2B-blue?style=for-the-badge&logo=python&logoColor=white" alt="Python">
46
+ <img src="https://img.shields.io/badge/Platform-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey?style=for-the-badge&logo=linux&logoColor=FCC624" alt="Platform">
47
+ <img src="https://img.shields.io/badge/code%20style-black-000000?style=for-the-badge" alt="black">
48
+ <img src="https://img.shields.io/badge/linting-ruff-orange?style=for-the-badge" alt="ruff">
49
+ <img src="https://img.shields.io/badge/type%20checked-pyright-4B8BBE?style=for-the-badge" alt="pyright">
50
+ </p>
51
+
52
+ ---
53
+
54
+ Encrypted channel library for headless projects. Built for speed and zero-trust networking.
55
+
56
+ Stack: TLS 1.2+ · X25519 · AESGCM-256 · HMAC-SHA256 · PBKDF2 · HKDF
57
+
58
+ ---
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install fognode
64
+ ```
65
+
66
+ ## Quick start (aiogram-style)
67
+
68
+ ### Server
69
+
70
+ ```python
71
+ from fognode import App, Cipher
72
+
73
+ app = App(
74
+ host="0.0.0.0",
75
+ port=9443,
76
+ user="alice",
77
+ password="secret",
78
+ cipher=Cipher.AESGCM,
79
+ )
80
+
81
+ @app.on_message()
82
+ async def echo(ctx):
83
+ await ctx.answer(f"echo: {ctx.message.text}")
84
+
85
+ @app.on_command("ping")
86
+ async def ping(ctx):
87
+ await ctx.answer("pong")
88
+
89
+ @app.on_connect()
90
+ async def on_connect(ctx):
91
+ print(f"+ {ctx.user}")
92
+
93
+ if __name__ == "__main__":
94
+ app.run()
95
+ ```
96
+
97
+ ### Client
98
+
99
+ ```python
100
+ from fognode import App, Cipher
101
+
102
+ app = App.client(
103
+ connect_string="alice@oak-pine-stone-field:9443",
104
+ password="secret",
105
+ cipher=Cipher.AESGCM,
106
+ )
107
+
108
+ @app.on_message()
109
+ async def on_message(ctx):
110
+ print(f"{ctx.message.user}: {ctx.message.text}")
111
+
112
+ if __name__ == "__main__":
113
+ app.run()
114
+ ```
115
+
116
+ ## Classic API
117
+
118
+ ```python
119
+ from fognode import start_server, client_connect
120
+
121
+ ip, code, fp = start_server("0.0.0.0", 9443, "alice", "secret")
122
+ print(f"Connect: alice@{code}:9443")
123
+ ```
124
+
125
+ ## Structure
126
+
127
+ ```
128
+ src/fognode/
129
+ ├── app.py # App class (aiogram-style)
130
+ ├── cipher.py # Cipher enum
131
+ ├── context.py # Context for handlers
132
+ ├── message.py # Message dataclass
133
+ ├── router.py # Router for handlers
134
+ ├── filters/ # Command, Text filters
135
+ ├── handlers/ # HandlerObject
136
+ ├── types/ # exceptions, constants, protocol
137
+ ├── crypto/ # primitives, kdf, cert, channel
138
+ ├── ciphers/ # aesgcm, chacha20, x25519, hkdf, pbkdf2, hmac
139
+ ├── wire/ # framing
140
+ ├── auth/ # handshake
141
+ ├── core/ # server, client, session, state
142
+ ├── decorators.py # retry, rate_limited, timed
143
+ ├── exceptions.py # errors
144
+ ├── utils/ # ipwords, ratelimit, net
145
+ └── cli/ # entrypoint
146
+ ```
147
+
148
+ ---
149
+
150
+ <p align="center"><sub><a href="LICENSE">MIT</a> © reekeer</sub></p>
@@ -0,0 +1,112 @@
1
+ <h1 align="center">fognode</h1>
2
+
3
+ <h4 align="center">headless secure encrypted data transmission.</h4>
4
+
5
+ <p align="center">
6
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge&logo=opensourceinitiative&logoColor=FFFFFF" alt="License"></a>
7
+ <img src="https://img.shields.io/badge/Python-3.10%2B-blue?style=for-the-badge&logo=python&logoColor=white" alt="Python">
8
+ <img src="https://img.shields.io/badge/Platform-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey?style=for-the-badge&logo=linux&logoColor=FCC624" alt="Platform">
9
+ <img src="https://img.shields.io/badge/code%20style-black-000000?style=for-the-badge" alt="black">
10
+ <img src="https://img.shields.io/badge/linting-ruff-orange?style=for-the-badge" alt="ruff">
11
+ <img src="https://img.shields.io/badge/type%20checked-pyright-4B8BBE?style=for-the-badge" alt="pyright">
12
+ </p>
13
+
14
+ ---
15
+
16
+ Encrypted channel library for headless projects. Built for speed and zero-trust networking.
17
+
18
+ Stack: TLS 1.2+ · X25519 · AESGCM-256 · HMAC-SHA256 · PBKDF2 · HKDF
19
+
20
+ ---
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install fognode
26
+ ```
27
+
28
+ ## Quick start (aiogram-style)
29
+
30
+ ### Server
31
+
32
+ ```python
33
+ from fognode import App, Cipher
34
+
35
+ app = App(
36
+ host="0.0.0.0",
37
+ port=9443,
38
+ user="alice",
39
+ password="secret",
40
+ cipher=Cipher.AESGCM,
41
+ )
42
+
43
+ @app.on_message()
44
+ async def echo(ctx):
45
+ await ctx.answer(f"echo: {ctx.message.text}")
46
+
47
+ @app.on_command("ping")
48
+ async def ping(ctx):
49
+ await ctx.answer("pong")
50
+
51
+ @app.on_connect()
52
+ async def on_connect(ctx):
53
+ print(f"+ {ctx.user}")
54
+
55
+ if __name__ == "__main__":
56
+ app.run()
57
+ ```
58
+
59
+ ### Client
60
+
61
+ ```python
62
+ from fognode import App, Cipher
63
+
64
+ app = App.client(
65
+ connect_string="alice@oak-pine-stone-field:9443",
66
+ password="secret",
67
+ cipher=Cipher.AESGCM,
68
+ )
69
+
70
+ @app.on_message()
71
+ async def on_message(ctx):
72
+ print(f"{ctx.message.user}: {ctx.message.text}")
73
+
74
+ if __name__ == "__main__":
75
+ app.run()
76
+ ```
77
+
78
+ ## Classic API
79
+
80
+ ```python
81
+ from fognode import start_server, client_connect
82
+
83
+ ip, code, fp = start_server("0.0.0.0", 9443, "alice", "secret")
84
+ print(f"Connect: alice@{code}:9443")
85
+ ```
86
+
87
+ ## Structure
88
+
89
+ ```
90
+ src/fognode/
91
+ ├── app.py # App class (aiogram-style)
92
+ ├── cipher.py # Cipher enum
93
+ ├── context.py # Context for handlers
94
+ ├── message.py # Message dataclass
95
+ ├── router.py # Router for handlers
96
+ ├── filters/ # Command, Text filters
97
+ ├── handlers/ # HandlerObject
98
+ ├── types/ # exceptions, constants, protocol
99
+ ├── crypto/ # primitives, kdf, cert, channel
100
+ ├── ciphers/ # aesgcm, chacha20, x25519, hkdf, pbkdf2, hmac
101
+ ├── wire/ # framing
102
+ ├── auth/ # handshake
103
+ ├── core/ # server, client, session, state
104
+ ├── decorators.py # retry, rate_limited, timed
105
+ ├── exceptions.py # errors
106
+ ├── utils/ # ipwords, ratelimit, net
107
+ └── cli/ # entrypoint
108
+ ```
109
+
110
+ ---
111
+
112
+ <p align="center"><sub><a href="LICENSE">MIT</a> © reekeer</sub></p>