0.1.1
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
Server Side Public License (SSPL) v1.0
|
||||||
|
|
||||||
|
Copyright © 2025 Roman Nikolskii (romb.intu@gmail.com)
|
||||||
|
|
||||||
|
NOTICE: This license imposes commercial use restrictions for offering this software as a service. Consult legal advice before deployment.
|
||||||
|
1. Definitions
|
||||||
|
"Software": The godpn project and its derivative works.
|
||||||
|
"Service": Any offering allowing third parties to interact with the Software’s functionality via a network.
|
||||||
|
|
||||||
|
2. Permissions
|
||||||
|
|
||||||
|
Subject to compliance with this license, you are granted:
|
||||||
|
✅ Use: Run the Software for any purpose, private or commercial.
|
||||||
|
✅ Modification: Create derivative works.
|
||||||
|
✅ Distribution: Redistribute source or object code.
|
||||||
|
3. Restrictions
|
||||||
|
|
||||||
|
❌ No Commercial SaaS: You may not offer the Software (or modifications) as a publicly available Service without:
|
||||||
|
Open-sourcing all modifications and infrastructure code under SSPL, or
|
||||||
|
Obtaining a commercial license from the copyright holder (Roman Nikolskii).
|
||||||
|
|
||||||
|
❌ No Circumvention: You may not bypass technical enforcement mechanisms.
|
||||||
|
4. Service Provider Requirements
|
||||||
|
|
||||||
|
If you operate a Service using this Software, you must:
|
||||||
|
Disclose Source: Make available to all users:
|
||||||
|
The complete source code of the Software, including all modifications.
|
||||||
|
The source code of all system components needed to run the Service (e.g., management software, APIs).
|
||||||
|
Offer License: Provide instructions for users to obtain the source under SSPL.
|
||||||
|
|
||||||
|
5. Attribution
|
||||||
|
|
||||||
|
All copies must retain:
|
||||||
|
This license file.
|
||||||
|
Copyright notices in source files.
|
||||||
|
|
||||||
|
6. Disclaimer
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR DAMAGES ARISING FROM ITS USE.
|
||||||
|
7. License Compatibility
|
||||||
|
Third-party dependencies (e.g., go-shadowsocks2) remain under their original licenses (e.g., Apache 2.0).
|
||||||
|
This license applies only to original code authored by Roman Nikolskii in the godpn project.
|
||||||
|
|
||||||
|
8. Contact
|
||||||
|
|
||||||
|
For commercial licensing inquiries:
|
||||||
|
📧 Roman Nikolskii – romb.intu@gmail.com
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# GODPN - Decentralized Private Network
|
||||||
|
|
||||||
|
## 🏛️ Архитектура
|
||||||
|
1. Компоненты системы
|
||||||
|
|
||||||
|
1.1 Трекер (Coordinator Server)
|
||||||
|
|
||||||
|
Роль: Управляет пирами, собирает метрики, назначает оптимальные подключения.
|
||||||
|
|
||||||
|
Функции:
|
||||||
|
|
||||||
|
Регистрирует пиры (IP, порт, доступная пропускная способность, геолокация).
|
||||||
|
Собирает статистику: скорость, пинг, трафик.
|
||||||
|
Ранжирует пиры для клиентов (по скорости/стабильности).
|
||||||
|
Начисляет монеты за трафик, списывает их у клиентов.
|
||||||
|
|
||||||
|
Технологии:
|
||||||
|
|
||||||
|
Echo (HTTP API для клиентов и пиров).
|
||||||
|
gRPC (для быстрого обмена метриками).
|
||||||
|
Redis (кеш рейтингов пиров).
|
||||||
|
PostgreSQL/SQLite3 (хранение данных пользователей, транзакций).
|
||||||
|
|
||||||
|
1.2. Клиент (VPN Client)
|
||||||
|
|
||||||
|
Роль: Подключается к пирам или центральным серверам через Shadowsocks.
|
||||||
|
|
||||||
|
Функции:
|
||||||
|
|
||||||
|
Получает список пиров от трекера.
|
||||||
|
Тестирует подключение (ping, speedtest).
|
||||||
|
Выбирает лучший пир/сервер.
|
||||||
|
Учитывает потраченные/заработанные монеты.
|
||||||
|
|
||||||
|
Технологии:
|
||||||
|
|
||||||
|
go-shadowsocks2 (подключение через SOCKS5).
|
||||||
|
libp2p (для P2P-подключений).
|
||||||
|
|
||||||
|
1.3. Пир (Peer Node)
|
||||||
|
|
||||||
|
Роль: Раздает трафик другим клиентам за монеты.
|
||||||
|
|
||||||
|
Функции:
|
||||||
|
|
||||||
|
Регистрируется на трекере.
|
||||||
|
Принимает подключения через Shadowsocks.
|
||||||
|
Отправляет метрики (трафик, скорость) трекеру.
|
||||||
|
Получает вознаграждение в монетах.
|
||||||
|
|
||||||
|
Технологии:
|
||||||
|
|
||||||
|
go-shadowsocks2 (режим сервера).
|
||||||
|
Prometheus + Grafana (мониторинг).
|
||||||
|
|
||||||
|
2. Как работает трафик
|
||||||
|
|
||||||
|
2.1. Подключение клиента
|
||||||
|
|
||||||
|
Клиент запрашивает у трекера список доступных пиров.
|
||||||
|
Трекер возвращает доступные пиры по скорости/стоимости.
|
||||||
|
Клиент тестирует их и подключается к лучшему.
|
||||||
|
Весь трафик идет через Shadowsocks-туннель (Можно настроить).
|
||||||
|
|
||||||
|
3. Монетизация
|
||||||
|
|
||||||
|
3.1. Внутренняя валюта
|
||||||
|
|
||||||
|
Монеты (токены) начисляются за отданный трафик.
|
||||||
|
Клиенты покупают подписку или тратят монеты на трафик.
|
||||||
|
|
||||||
|
## 📜 Лицензия
|
||||||
|
Этот проект распространяется под **SSPL-1.0**.
|
||||||
|
Коммерческое использование в качестве сервиса (SaaS) без разрешения автора запрещено.
|
||||||
|
|
||||||
|
Данная лицензия (SSPL) применяется только к коду, написанному автором godpn.
|
||||||
|
Сторонние библиотеки (например, go-shadowsocks2) используются под их исходными лицензиями.
|
||||||
+2
-2
@@ -3,11 +3,11 @@ package main
|
|||||||
import (
|
import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/rombintu/godpn/internal/tracker"
|
"github.com/rombintu/godpn/internal/coordinator"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
server := tracker.NewServer()
|
server := coordinator.NewServer()
|
||||||
server.Configure()
|
server.Configure()
|
||||||
if err := server.Run(); err != nil {
|
if err := server.Run(); err != nil {
|
||||||
slog.Info(err.Error())
|
slog.Info(err.Error())
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package coordinator
|
||||||
|
|
||||||
|
import "github.com/rombintu/godpn/internal/models"
|
||||||
|
|
||||||
|
type Coordinator struct {
|
||||||
|
// TODO временное решение
|
||||||
|
//
|
||||||
|
// ip : Peer
|
||||||
|
peers map[string]models.PeerNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCoordinator() *Coordinator {
|
||||||
|
return &Coordinator{
|
||||||
|
peers: make(map[string]models.PeerNode),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package tracker
|
package coordinator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -9,27 +9,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) registerHandler(c echo.Context) error {
|
func (s *Server) registerHandler(c echo.Context) error {
|
||||||
var peer models.PeerManifest
|
var node models.PeerNode
|
||||||
if err := c.Bind(peer); err != nil {
|
if err := c.Bind(node); err != nil {
|
||||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if peer.PubKey == "" {
|
if node.PublicKey == "" {
|
||||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "pubkey is required"})
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "public key is required"})
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := c.RealIP()
|
ip := c.RealIP()
|
||||||
if peer.IP != "" {
|
if node.Endpoint != "" {
|
||||||
ip = peer.IP
|
ip = node.Endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.tracker.peers[ip] = peer.PubKey
|
s.coordinator.peers[ip] = node
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, map[string]string{
|
return c.JSON(http.StatusOK, map[string]models.PeerNode{
|
||||||
"status": "registered",
|
"registered": node,
|
||||||
"ip": ip,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ func (s *Server) registerHandler(c echo.Context) error {
|
|||||||
func (s *Server) listPeers(c echo.Context) error {
|
func (s *Server) listPeers(c echo.Context) error {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
return c.JSON(http.StatusOK, s.tracker.peers)
|
return c.JSON(http.StatusOK, s.coordinator.peers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebSocket handler для реального времени
|
// WebSocket handler для реального времени
|
||||||
@@ -54,7 +53,7 @@ func (s *Server) websocketHandler(c echo.Context) error {
|
|||||||
|
|
||||||
// Отправляем текущий список peers при подключении
|
// Отправляем текущий список peers при подключении
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
if err := ws.WriteJSON(s.tracker.peers); err != nil {
|
if err := ws.WriteJSON(s.coordinator.peers); err != nil {
|
||||||
s.mu.RUnlock()
|
s.mu.RUnlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package tracker
|
package coordinator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
@@ -8,15 +8,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
router *echo.Echo
|
router *echo.Echo
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
tracker *Tracker
|
coordinator *Coordinator
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer() *Server {
|
func NewServer() *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
router: echo.New(),
|
router: echo.New(),
|
||||||
tracker: NewTracker(),
|
coordinator: NewCoordinator(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+26
-3
@@ -1,6 +1,29 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type PeerManifest struct {
|
import "time"
|
||||||
IP string `json:"ip" form:"ip" query:"ip"`
|
|
||||||
PubKey string `json:"pubkey" form:"pubkey" query:"pubkey" validate:"required"`
|
type PeerNode struct {
|
||||||
|
// Basic identity
|
||||||
|
PublicKey string `json:"public_key" validate:"required"` // WireGuard/SS public key
|
||||||
|
|
||||||
|
// Network information
|
||||||
|
Endpoint string `json:"endpoint"` // IP:Port or DNS
|
||||||
|
Protocol string `json:"protocol"` // "shadowsocks", "wireguard", etc
|
||||||
|
|
||||||
|
// Performance metrics
|
||||||
|
System string `json:"system"` // "linux", "windows", etc
|
||||||
|
Latency int `json:"latency"` // in milliseconds
|
||||||
|
Bandwidth int `json:"bandwidth"` // in Mbps
|
||||||
|
|
||||||
|
// Geo information
|
||||||
|
Region string `json:"region,omitempty"` // "eu-west", "us-east"
|
||||||
|
Country string `json:"country,omitempty"` // 2-letter ISO code
|
||||||
|
|
||||||
|
// System stats
|
||||||
|
Uptime float64 `json:"uptime"` // 0.0-1.0 percentage
|
||||||
|
Load float64 `json:"load"` // 5-min system load avg
|
||||||
|
|
||||||
|
// Timestamps
|
||||||
|
LastSeen time.Time `json:"last_seen"`
|
||||||
|
FirstSeen time.Time `json:"first_seen"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) connectHandler(c echo.Context) error {
|
func (s *Server) connectHandler(c echo.Context) error {
|
||||||
var peerManifest models.PeerManifest
|
var node models.PeerNode
|
||||||
|
|
||||||
if err := c.Bind(peerManifest); err != nil {
|
if err := c.Bind(node); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
peer := NewPeer(peerManifest.PubKey)
|
peer := NewPeer(node.PublicKey)
|
||||||
if err := peer.Connect(peerManifest.IP, "8080"); err != nil {
|
if err := peer.Connect(node.Endpoint); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer peer.Disconnect()
|
defer peer.Disconnect()
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tcpNetwork = "tcp"
|
tcpNetwork = "tcp"
|
||||||
bufferSize = 32 * 1024 // 32KB
|
bufferSize = 32 * 1024 // 32KB
|
||||||
|
AHEAD_CHACHA20_POLY1305 = "AEAD_CHACHA20_POLY1305" // Default method cipher
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ошибки подключения
|
// Ошибки подключения
|
||||||
@@ -34,7 +35,7 @@ func NewPeer(pubKey string) *Peer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Peer) Connect(ip, port string) error {
|
func (p *Peer) Connect(endpoint string) error {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
@@ -42,12 +43,12 @@ func (p *Peer) Connect(ip, port string) error {
|
|||||||
return nil // Уже подключен
|
return nil // Уже подключен
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := net.Dial(tcpNetwork, net.JoinHostPort(ip, port))
|
conn, err := net.Dial(tcpNetwork, endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cipher, err := core.PickCipher("AEAD_CHACHA20_POLY1305", []byte(p.pubKey), "")
|
cipher, err := core.PickCipher(AHEAD_CHACHA20_POLY1305, []byte(p.pubKey), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package tracker
|
|
||||||
|
|
||||||
type Tracker struct {
|
|
||||||
peers map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTracker() *Tracker {
|
|
||||||
return &Tracker{
|
|
||||||
peers: make(map[string]string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user