{"id":13478061,"url":"https://github.com/gabotechs/signway","last_synced_at":"2025-04-11T17:43:12.236Z","repository":{"id":171367942,"uuid":"647840310","full_name":"gabotechs/signway","owner":"gabotechs","description":"Bring the power of pre-signed URLs to your apps. Signway is a gateway for redirecting ephimeral signed URLs to the requested API","archived":false,"fork":false,"pushed_at":"2024-05-10T19:24:38.000Z","size":1406,"stargazers_count":94,"open_issues_count":1,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-06T07:38:11.812Z","etag":null,"topics":["api","gateway","llm","openai","rust","rust-lang","signature-verification","signed-url"],"latest_commit_sha":null,"homepage":"https://www.signway.io","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gabotechs.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-31T16:21:24.000Z","updated_at":"2025-02-25T23:21:53.000Z","dependencies_parsed_at":"2024-10-31T06:12:15.144Z","dependency_job_id":null,"html_url":"https://github.com/gabotechs/signway","commit_stats":null,"previous_names":["gabotechs/signway"],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabotechs%2Fsignway","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabotechs%2Fsignway/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabotechs%2Fsignway/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabotechs%2Fsignway/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gabotechs","download_url":"https://codeload.github.com/gabotechs/signway/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248451735,"owners_count":21105924,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["api","gateway","llm","openai","rust","rust-lang","signature-verification","signed-url"],"created_at":"2024-07-31T16:01:51.936Z","updated_at":"2025-04-11T17:43:12.214Z","avatar_url":"https://github.com/gabotechs.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg height=\"100\" src=\"docs/src/assets/signway-white.png\" alt=\"Signway logo\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://www.signway.io\"\u003e\n        \u003cimg src=\"https://img.shields.io/static/v1?label=website\u0026message=link\u0026color=purple\" alt=\"Signway Book\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://gabotechs.github.io/signway\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/book-WIP-4d76ae.svg\" alt=\"Signway Book\"/\u003e\n    \u003c/a\u003e\n    \u003cimg src=\"https://coveralls.io/repos/github/gabotechs/signway/badge.svg?branch=main\" alt=\"coverage badge\"/\u003e\n    \u003cimg src=\"https://img.shields.io/github/v/release/gabotechs/signway?color=%e535abff)\" alt=\"last release\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    Signway is a proxy server that addresses the problem of re-streaming third-party API responses from backend to frontend by allowing the frontend to \"directly\" request these APIs. Your backend code creates syncronously a short-lived signed URL that points to the third-party API, and your frontend code issues an HTTP query to that URL, which will query the third-party API safely proxying the request through Signway.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    Check the \u003ca href=\"https://gabotechs.github.io/signway/\"\u003edocs\u003c/a\u003e for more info. If you are looking for the managed version checkout \u003ca href=\"https://www.signway.io\"\u003ethis link\u003c/a\u003e.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg height=\"150px\" src=\"docs/src/assets/simple-scheme.png\" alt=\"Signway scheme\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ch1 align=\"center\"\u003eSign your requests and proxy them\u003c/h1\u003e\n\u003c/p\u003e\n\n\u003ctable align=\"center\"\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\n            \u003cth\u003e\n                \u003ccode\u003eTerminal 1\u003c/code\u003e: Launch Signway\n            \u003c/th\u003e\n            \u003cth\u003e\n                \u003ccode\u003eTerminal 2\u003c/code\u003e: Create Signed URLs\n            \u003c/th\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n        \u003ctr\u003e\n            \u003ctd\u003e \n\nExport the signing credentials for Signway:\n\n```bash\nexport SW_ID=\"app-id\"\nexport SW_SECRET=\"super-secure-string\"\n```\n\nExport your OpenAI's API key here:\n\n```bash\nexport API_KEY=\"your working token\"\n```\n\nLaunch Signway, ready for accepting requests signed\nwith `SW_ID` and `SW_SECRET`:\n\n```bash\ndocker run -p 3000:3000 gabotechs/signway \\\n$SW_ID \\\n$SW_SECRET \\\n--header \"Authorization: Bearer $API_KEY\"\n  \n```\n\nLeave Signway running in this terminal...\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n\u003c/td\u003e\u003ctd\u003e\n\n👈 Export the same credentials:\n\n```bash\nexport SW_ID=\"app-id\"\nexport SW_SECRET=\"super-secure-string\"\n```\nInstall Signway's Python sdk:\n\n```bash\npython3 -m venv venv\nsource venv/bin/activate\npip install signway-sdk\n```\n\nMake a script for creating signed URLs:\n\n```python\ncat \u003c\u003c 'EOF' \u003e sign.py\nfrom signway_sdk import sign_url\nimport os\n\nprint(sign_url(\n  os.environ['SW_ID'],\n  os.environ['SW_SECRET'],\n  \"http://localhost:3000\",\n  \"https://api.openai.com/v1/completions\",\n  10,  # \u003c- URL expiration time in s\n  \"POST\"\n))\nEOF\n```\n\nMake a request to the generated signed URL, as if it was OpenAI:\n\n```bash\ncurl $(python3 sign.py) \\\n-H \"Content-Type: application/json\" \\\n-d '{\n  \"model\": \"text-davinci-003\",\n  \"stream\": true,\n  \"prompt\": \"Say this is a test\"\n}'\n```\n\u003c/td\u003e\u003c/tr\u003e\u003c/tbody\u003e\u003c/table\u003e\n\n# What does it solve?\n\nImagine that you have a setup that looks like this. Your backend accesses\na public and authenticated api using an API token, and the response needs \nto be streamed in chunks, because it is a lot of data or because the response\nuses [SSE](https://www.w3schools.com/html/html5_serversentevents.asp).\n\n```mermaid\nsequenceDiagram\n    participant frontend\n    participant backend\n    participant third_party api\n    \n    frontend-\u003e\u003e+backend: request\n    backend-\u003e\u003ebackend: authentication\n    backend-\u003e\u003e+third_party api: request + API token\n    third_party api-\u003e\u003e-backend: data stream\n    backend-\u003e\u003e-frontend: data stream\n```\n\nAs you own the backend, you can safely configure there any api tokens needed for\nauthenticating against the third party API, and re-stream back the data\nas it comes to the end user.\n\nHowever, if you are using a **serverless** architecture, this gets **tricky** for two\nreasons:\n1. Most serverless setups, like [AWS lamda](https://aws.amazon.com/lambda/) with [API Gateway](https://aws.amazon.com/api-gateway/),\n**don't allow you to stream the response**, you can only send back one final blob.\n2. Your serverless function would need to live for a very long time, even if it is just\ndoing slow IO data transfer, so **cost may increase significantly**.\n\nThis is where Signway enters the game. Signway provides you a way of letting the\nend user do the request \"almost directly\" to the third party API in a secure way\nwithout the need of leaking credentials.\n\nThe schema using Signway looks like this:\n\n```mermaid\nsequenceDiagram\n    participant frontend\n    participant serverless backend\n    participant Signway\n    participant third_party api\n\n    frontend-\u003e\u003e+serverless backend: request\n    serverless backend-\u003e\u003eserverless backend: authentication\n    serverless backend-\u003e\u003eserverless backend: create signed url using an \"id\" and a \"secret\"\n    serverless backend-\u003e\u003e-frontend: signed URL\n    frontend-\u003e\u003e+Signway: signed URL + request\n    Signway-\u003e\u003eSignway: verify signature for \"id\" using \"secret\"\n    Signway-\u003e\u003e+third_party api: request + API token\n    third_party api-\u003e\u003e-Signway: data stream\n    Signway-\u003e\u003e-frontend: data stream\n    \n```\n\nThis way you leverage heavy IO work to Signway, which is a high performant gateway server\nwritten in Rust prepared for heavy throughput, and you are able to stream data to end users\nfrom APIs that send you data chunk by chunk.\n\n# Signing algorithm\n\nThe signing algorithm is inspired strongly by [AWS signature v4](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html),\nthe same that [s3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html)\nuses for generating pre-signed URLs for letting clients interact with buckets directly.\n\nGenerating a signed URL requires that the signer (usually an application's backend) knows a public `id` and a private `secret`. \nThe `id` will live in plain text in the signed URL, and the `secret` will be used for creating the request's\nsignature, but it is not included in the URL.\n\nSignway, who knows which `secret` is associated to which `id`, will take the request and\nrecalculate its signature. If the declared signature and the calculated one match, and the request has not expired,\nit will proxy the request to the specified third party API, adding any preconfigured headers for that `id`, like API tokens.\n\n\n# Usage\n\nThe server is meant to be used with docker, there are public images with support for\n`linux/arm64` and `linux/amd64`.\n\n```shell\ndocker run gabotechs/signway my-id my-secret\n```\n\nYou can also declare the headers that will be automatically added to the proxy-ed request in case\nof an authentic signed request is received:\n\n```shell\ndocker run gabotechs/signway my-id my-secret -h 'Authorization:Bearer my-secret-token'\n```\n\n# Pre-signed URL generation\n\nTypically, the Signway server will be publicly accessible to the internet, prepared\nto accept requests with a pre-signed URL, but someone needs to create those pre-signed URLs. This\nshould be a trusted source, as it needs to know the signing `secret`, like an application's backend.\n\nThere is support for generating Signway pre-signed URLs for the following languages:\n- [Python](https://github.com/gabotechs/signway-python-sdk)\n- [JavaScript](https://github.com/gabotechs/signway-js-sdk)\n\nIn order to generate a pre-signed URL, knowing the `id` and the `secret` that\nSignway is expecting is necessary, and as the `secret` is private, it is important\nto be careful not to leak it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabotechs%2Fsignway","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgabotechs%2Fsignway","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabotechs%2Fsignway/lists"}