{"id":13509910,"url":"https://github.com/nanopack/portal","last_synced_at":"2026-02-14T14:14:39.903Z","repository":{"id":142963865,"uuid":"45642433","full_name":"nanopack/portal","owner":"nanopack","description":"An api-driven, in-kernel layer 2/3 load balancer.","archived":false,"fork":false,"pushed_at":"2019-03-11T21:41:02.000Z","size":292,"stargazers_count":106,"open_issues_count":2,"forks_count":11,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-08-14T12:00:12.286Z","etag":null,"topics":["balancer","developer-tools","devops","devtools","golang","load-balancer","nanobox","nanopack","proxy"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nanopack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-11-05T22:05:06.000Z","updated_at":"2025-03-16T17:41:11.000Z","dependencies_parsed_at":"2023-04-13T07:34:45.659Z","dependency_job_id":null,"html_url":"https://github.com/nanopack/portal","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/nanopack/portal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanopack%2Fportal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanopack%2Fportal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanopack%2Fportal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanopack%2Fportal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nanopack","download_url":"https://codeload.github.com/nanopack/portal/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanopack%2Fportal/sbom","scorecard":{"id":674069,"data":{"date":"2025-08-11","repo":{"name":"github.com/nanopack/portal","commit":"d165bff1df343d390432e078a69f715667ad004a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 1/15 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.1.1 not signed: https://api.github.com/repos/nanopack/portal/releases/7840568","Warn: release artifact v0.1.1 does not have provenance: https://api.github.com/repos/nanopack/portal/releases/7840568"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 22 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T21:00:11.744Z","repository_id":142963865,"created_at":"2025-08-21T21:00:11.744Z","updated_at":"2025-08-21T21:00:11.744Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29446655,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T14:10:32.461Z","status":"ssl_error","status_checked_at":"2026-02-14T14:09:49.945Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["balancer","developer-tools","devops","devtools","golang","load-balancer","nanobox","nanopack","proxy"],"created_at":"2024-08-01T02:01:16.790Z","updated_at":"2026-02-14T14:14:39.887Z","avatar_url":"https://github.com/nanopack.png","language":"Go","funding_links":[],"categories":["Go","proxy"],"sub_categories":[],"readme":"# Portal \n[![Build Status](https://travis-ci.org/nanopack/portal.svg)](https://travis-ci.org/nanopack/portal)\n[![GoDoc](https://godoc.org/github.com/nanopack/portal?status.svg)](https://godoc.org/github.com/nanopack/portal)\n\nPortal is an api-driven, in-kernel layer 2/3 load balancer.\n\n## Status\nComplete/Experimental\n\n## Usage:\n\n### As a CLI\nSimply run `portal \u003cCOMMAND\u003e`\n\n`portal` or `portal -h` will show usage and a list of commands:\n\n```\nportal - load balancer/proxy\n\nUsage:\n  portal [flags]\n  portal [command]\n\nAvailable Commands:\n  add-service    Add service\n  remove-service Remove service\n  show-service   Show service\n  show-services  Show all services\n  set-services   Set service list\n  set-service    Set service\n  add-server     Add server to a service\n  remove-server  Remove server from a service\n  show-server    Show server on a service\n  show-servers   Show all servers on a service\n  set-servers    Set server list on a service\n  add-route      Add route\n  set-routes     Set route list\n  show-routes    Show all routes\n  remove-route   Remove route\n  add-cert       Add cert\n  set-certs      Set cert list\n  show-certs     Show all certs\n  remove-cert    Remove cert\n  add-vip        Add vip\n  set-vips       Set vip list\n  show-vips      Show all vips\n  remove-vip     Remove vip\n\nFlags:\n  -C, --api-cert=\"\": SSL cert for the api\n  -H, --api-host=\"127.0.0.1\": Listen address for the API\n  -k, --api-key=\"\": SSL key for the api\n  -p, --api-key-password=\"\": Password for the SSL key\n  -P, --api-port=\"8443\": Listen address for the API\n  -t, --api-token=\"\": Token for API Access\n  -b, --balancer=\"lvs\": Load balancer to use (nginx|lvs)\n  -r, --cluster-connection=\"none://\": Cluster connection string (redis://127.0.0.1:6379)\n  -T, --cluster-token=\"\": Cluster security token\n  -c, --conf=\"\": Configuration file to load\n  -d, --db-connection=\"scribble:///var/db/portal\": Database connection string\n  -i, --insecure[=false]: Disable tls key checking (client) and listen on http (server)\n  -j, --just-proxy[=false]: Proxy only (no tcp/udp load balancing)\n  -L, --log-file=\"\": Log file to write to\n  -l, --log-level=\"INFO\": Log level to output\n  -x, --proxy-http=\"0.0.0.0:80\": Address to listen on for proxying http\n  -X, --proxy-tls=\"0.0.0.0:443\": Address to listen on for proxying https\n  -s, --server[=false]: Run in server mode\n  -v, --version[=false]: Print version info and exit\n  -w, --work-dir=\"/var/db/portal\": Directory for portal to use (balancer config)\n\nUse \"portal [command] --help\" for more information about a command.\n```\n\nFor usage examples, see [Api](api/README.md) and/or [Cli](commands/README.md) readme  \n\n### As a Server\nTo start portal as a server run:\n\n`portal --server`\n\nAn optional config file can also be passed on startup:\n\n`portal -c /path/to/config.json`\n\n\u003e config.json\n```json\n{\n  \"api-token\": \"\",\n  \"api-host\": \"127.0.0.1\",\n  \"api-port\": 8443,\n  \"api-key\": \"\",\n  \"api-cert\": \"\",\n  \"api-key-password\": \"\",\n  \"db-connection\": \"scribble:///var/db/portal\",\n  \"cluster-connection\": \"none://\",\n  \"cluster-token\": \"\",\n  \"insecure\": false,\n  \"just-proxy\": false,\n  \"proxy-http\": \"0.0.0.0:80\",\n  \"proxy-tls\": \"0.0.0.0:443\",\n  \"balancer\": \"nginx\",\n  \"work-dir\": \"/var/db/portal\",\n  \"log-level\": \"INFO\",\n  \"log-file\": \"\",\n  \"server\": true\n}\n```\n\n## API:\n\n| Route | Description | payload | output |\n| --- | --- | --- | --- |\n| **Get** /services | List all services | nil | json array of service objects |\n| **Post** /services | Add a service | json service object | json service object |\n| **Put** /services | Reset the list of services | json array of service objects | json array of service objects |\n| **Put** /services/:service_id | Reset the specified service | nil | json service object |\n| **Get** /services/:service_id | Get information about a service | nil | json service object |\n| **Delete** /services/:service_id | Delete a service | nil | success message or an error |\n| **Get** /services/:service_id/servers | List all servers on a service | nil | json array of server objects |\n| **Post** /services/:service_id/servers | Add new server to a service | json server object | json server object |\n| **Put** /services/:service_id/servers | Reset the list of servers on a service | json array of server objects | json array of server objects |\n| **Get** /services/:service_id/servers/:server_id | Get information about a server on a service | nil | json server object |\n| **Delete** /services/:service_id/servers/:server_id | Delete a server from a service | nil | success message or an error |\n| **Delete** /routes | Delete a route | subdomain, domain, and path (json or query) | success message or an error |\n| **Get** /routes | List all routes | nil | json array of route objects |\n| **Post** /routes | Add new route | json route object | json route object |\n| **Put** /routes | Reset the list of routes | json array of route objects | json array of route objects |\n| **Delete** /certs | Delete a cert | json cert object | success message or an error |\n| **Get** /certs | List all certs | nil | json array of cert objects |\n| **Post** /certs | Add new cert | json cert object | json cert object |\n| **Put** /certs | Reset the list of certs | json array of cert objects | json array of cert objects |\n| **Delete** /vips | Delete a vip | json vip object | success message or an error |\n| **Get** /vips | List all vips | nil | json array of vip objects |\n| **Post** /vips | Add new vip | json vip object | json vip object |\n| **Put** /vips | Reset the list of vips | json array of vip objects | json array of vip objects |\n\n- **service_id** is a formatted combination of service info: type-host-port. (tcp-127_0_0_3-80)  \n- **server_id** is a formatted combination of server info: host-port. (192_0_0_3-8080)  \n\nFor examples, see [the api's readme](api/README.md)  \n\n## Data types:\n### Service:\njson:\n```json\n{\n  \"host\": \"127.0.0.1\",\n  \"port\": 1234,\n  \"type\": \"tcp\",\n  \"scheduler\": \"wlc\",\n  \"persistence\": 300,\n  \"netmask\": \"255.255.255.0\",\n  \"servers\": []\n}\n```\n\njson with a server:\n```json\n{\n  \"host\": \"127.0.0.1\",\n  \"port\": 8080,\n  \"type\": \"tcp\",\n  \"scheduler\": \"wlc\",\n  \"persistence\": 300,\n  \"netmask\": \"\",\n  \"servers\": [\n    {\n      \"host\": \"172.28.128.4\",\n      \"port\": 8081,\n      \"forwarder\": \"m\",\n      \"weight\": 1,\n      \"UpperThreshold\": 0,\n      \"LowerThreshold\": 0\n    }\n  ]\n}\n```\n\nFields:\n- **host**: IP of the host the service is bound to.\n- **interface**: Host interface the service is bound to (optional, overrides host).\n- **port**: Port that the service listens to.\n- **type**: Type of service.\n  - tcp\n  - udp\n- **scheduler**: How to pick downstream server.\n  - rr    - Round Robin\n  - wrr   - Weighted Round Robin\n  - lc    - Least-Connection\n  - wlc   - Weighted Least-Connection\n  - lblc  - Locality-Based Least-Connection\n  - lblcr - Locality-Based Least-Connection with Replication\n  - dh    - Destination Hashing\n  - sh    - Source Hashing\n  - sed   - Shortest Expected Delay\n  - nq    - Never Queue\n- **persistence**: Timeout for keeping requests from the same client going to the same server.\n- **netmask**: How to group clients with persistence to servers.\n- **servers**: Array of server objects associated to the service.\n\n### Server:\njson:\n```json\n{\n  \"host\": \"127.0.0.1\",\n  \"port\": 1234,\n  \"forwarder\": \"m\",\n  \"weight\": 1,\n  \"upper_threshold\": 0,\n  \"lower_threshold\": 0\n}\n```\n\nFields:\n- **host**: IP of the host the service is bound to.\n- **port**: Port that the service listens to.\n- **forwarder**: Method to use to forward traffic to this server.\n  - g - Gatewaying\n  - i - Ipip encapsulation\n  - m - Masquerading\n- **weight**: Weight to perfer this server. Set to 0 if no traffic should go to this server.\n- **upper_threshold**: Stop sending connections to this server when this number is reached.\n  - 0 - No limit\n- **lower_threshold**: Restart sending connections when drains down to this number.\n  - 0 - Not set\n\n### Vip:\njson:\n```json\n{\n  \"ip\": \"192.168.0.101/24\",\n  \"interface\": \"eth1\",\n  \"alias\": \"eth1:0\"\n}\n```\n\nFields:\n - **ip**: Ip to add to interface. Should be `ip/cidr`\n - **interface**: Interface to add ip to\n - **alias**: Alias to assign the ip (can be used as \"interface\" when adding a service)\n\n### Route:\njson:\n```json\n{\n  \"subdomain\": \"admin\",\n  \"domain\": \"test.com\",\n  \"path\": \"/admin*\",\n  \"targets\": [\"http://127.0.0.1:8080/app1\",\"http://127.0.0.2\"],\n  \"fwdpath\": \"/\",\n  \"page\": \"\"\n}\n```\n\nFields:\n - **subdomain**: Subdomain to match on\n - **domain**: Domain to match on\n - **path**: Path to match on\n - **targets**: URIs of servers\n - **fwdpath**: Path to forward to targets (combined with target path)\n - **page**: Page to serve instead of routing to targets\n\n### Certificate:\njson:\n```json\n{\n  \"key\": \"-----BEGIN PRIVATE KEY-----\\nMII.../J8\\n-----END PRIVATE KEY-----\",\n  \"cert\": \"-----BEGIN CERTIFICATE-----\\nMII...aI=\\n-----END CERTIFICATE-----\"\n}\n```\n\nFields:\n - **key**: Pem style key\n - **cert**: Pem style certificate\n\n### Error:\njson:\n```json\n{\n  \"error\": \"exit status 2: unexpected argument\"\n}\n```\n\nFields:\n - **error**: Error message\n\n### Message:\njson:\n```json\n{\n  \"msg\": \"Success\"\n}\n```\n\nFields:\n - **msg**: Success message\n\n### Contributing\nPortal uses [`govendor`](https://github.com/kardianos/govendor) to manage dependencies. After clone, run `govendor sync` to ensure you have the proper dependencies prior to building.\n\n## Todo\n- vip testing\n- balance vips across cluster\n- redis cluster init to get and set certs and vips\n\n## Changelog\n- 0.1.1 - 17-06-20\n  - Log and fail if db init fails\n  - Use govendor for deps\n  - Pre-validate protocol for services\n  - Log all 'forwarded-for' ips\n\n- 0.1.0 - 17-01-24\n  - Fixed start issues related to VIPs\n\n- Added ability to specify interface when registering a service\n- Users can now add vips to portal\n- Portal now uses the service host for server's hosts that are \"127.0.0.1\"\n\n[![portal logo](http://nano-assets.gopagoda.io/open-src/nanobox-open-src.png)](http://nanobox.io/open-source)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanopack%2Fportal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnanopack%2Fportal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanopack%2Fportal/lists"}