{"id":39534787,"url":"https://github.com/vcabbage/trivialt","last_synced_at":"2026-01-18T06:32:20.629Z","repository":{"id":57528272,"uuid":"57424064","full_name":"vcabbage/trivialt","owner":"vcabbage","description":"Go TFTP library with full standards support.","archived":false,"fork":false,"pushed_at":"2017-03-06T01:01:20.000Z","size":1173,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-11T04:24:10.531Z","etag":null,"topics":["golang","tftp"],"latest_commit_sha":null,"homepage":"","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/vcabbage.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}},"created_at":"2016-04-30T02:41:34.000Z","updated_at":"2019-12-28T00:10:47.000Z","dependencies_parsed_at":"2022-08-30T11:50:28.695Z","dependency_job_id":null,"html_url":"https://github.com/vcabbage/trivialt","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/vcabbage/trivialt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vcabbage%2Ftrivialt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vcabbage%2Ftrivialt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vcabbage%2Ftrivialt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vcabbage%2Ftrivialt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vcabbage","download_url":"https://codeload.github.com/vcabbage/trivialt/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vcabbage%2Ftrivialt/sbom","scorecard":{"id":917282,"data":{"date":"2025-08-11","repo":{"name":"github.com/vcabbage/trivialt","commit":"46708afde8c6eae7951d19863c9ce3a3bdee0925"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"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 0/30 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":"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":"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":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: cmd/trivialt/trivialt:1"],"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":"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":"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":"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":"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":"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":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.3.0-beta not signed: https://api.github.com/repos/vcabbage/trivialt/releases/5022123","Warn: release artifact v0.2.0-beta not signed: https://api.github.com/repos/vcabbage/trivialt/releases/3137906","Warn: release artifact v0.1.0 not signed: https://api.github.com/repos/vcabbage/trivialt/releases/3134586","Warn: release artifact v0.3.0-beta does not have provenance: https://api.github.com/repos/vcabbage/trivialt/releases/5022123","Warn: release artifact v0.2.0-beta does not have provenance: https://api.github.com/repos/vcabbage/trivialt/releases/3137906","Warn: release artifact v0.1.0 does not have provenance: https://api.github.com/repos/vcabbage/trivialt/releases/3134586"],"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 9 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-24T21:30:25.598Z","repository_id":57528272,"created_at":"2025-08-24T21:30:25.598Z","updated_at":"2025-08-24T21:30:25.598Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28531997,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["golang","tftp"],"created_at":"2026-01-18T06:32:20.567Z","updated_at":"2026-01-18T06:32:20.621Z","avatar_url":"https://github.com/vcabbage.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# **Deprecation**\n\n**The Go library portion of this project has moved to [pack.ag/tftp](https://pack.ag/tftp).**\n\nThis code remains here as to not break any projects depending on it.\n\n# trivialt\n\n[![Go Report Card](https://goreportcard.com/badge/vcabbage/trivialt)](https://goreportcard.com/report/vcabbage/trivialt)\n[![Coverage Status](https://coveralls.io/repos/github/vcabbage/trivialt/badge.svg?branch=master)](https://coveralls.io/github/vcabbage/trivialt?branch=master)\n[![Build Status](https://travis-ci.org/vcabbage/trivialt.svg?branch=master)](https://travis-ci.org/vcabbage/trivialt)\n[![Build status](https://ci.appveyor.com/api/projects/status/0sxw1t6jjoe4yc9p/branch/master?svg=true)](https://ci.appveyor.com/project/vcabbage/trivialt/branch/master)\n[![GoDoc](https://godoc.org/github.com/vcabbage/trivialt?status.svg)](http://godoc.org/github.com/vcabbage/trivialt)\n[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/vcabbage/trivialt/master/LICENSE)\n\n\ntrivialt is a cross-platform, concurrent TFTP server and client. It can be used as a standalone executable or included in a Go project as a library.\n\n\n### Standards Implemented\n\n- [X] Binary Transfer ([RFC 1350](https://tools.ietf.org/html/rfc1350))\n- [X] Netascii Transfer ([RFC 1350](https://tools.ietf.org/html/rfc1350))\n- [X] Option Extension ([RFC 2347](https://tools.ietf.org/html/rfc2347))\n- [X] Blocksize Option ([RFC 2348](https://tools.ietf.org/html/rfc2348))\n- [X] Timeout Interval Option ([RFC 2349](https://tools.ietf.org/html/rfc2349))\n- [X] Transfer Size Option ([RFC 2349](https://tools.ietf.org/html/rfc2349))\n- [X] Windowsize Option ([RFC 7440](https://tools.ietf.org/html/rfc7440))\n\n### Unique Features\n\n- __Single Port Mode__\n\n    TL;DR: It allows TFTP to work through firewalls.\n\n    A standard TFTP server implementation receives requests on port 69 and allocates a new high port (over 1024) dedicated to that request.\n    In single port mode, trivialt receives and responds to requests on the same port. If trivialt is started on port 69, all communication will\n    be done on port 69.\n    \n    The primary use case of this feature is to play nicely with firewalls. Most firewalls will prevent the typical case where the server responds\n    back on a random port because they have no way of knowing that it is in response to a request that went out on port 69. In single port mode,\n    the firewall will see a request go out to a server on port 69 and that server respond back on the same port, which most firewalls will allow.\n    \n    Of course if the firewall in question is configured to block TFTP connections, this setting won't help you.\n    \n    Enable single port mode with the `--single-port` flag. This is currently marked experimental as is diverges from the TFTP standard.\n\n## Installation\n\nIf you have the Go toolchain installed you can simply `go get` the packages. This will download the source into your `$GOPATH` and install the binary to `$GOPATH/bin/trivialt`.\n\n``` bash\ngo get -u github.com/vcabbage/trivialt/...\n```\n\nPre-built binaries can be downloaded from the [release page](https://github.com/vcabbage/trivialt/releases).\n\n## Command Usage\n\nRunning as a server:\n```\n# trivialt serve --help\nNAME:\n   trivialt serve - Serve files from the filesystem.\n\nUSAGE:\n   trivialt serve [bind address] [root directory]\n\nDESCRIPTION:\n   Serves files from the local file systemd.\n\n   Bind address is in form \"ip:port\". Omitting the IP will listen on all interfaces.\n   If not specified the server will listen on all interfaces, port 69.app\n\n   Files will be served from root directory. If omitted files will be served from\n   the current directory.\n\nOPTIONS:\n   --writeable, -w\t    Enable file upload.\n   --single-port, --sp\tEnable single port mode. [Experimental]\n```\n\n```\n# trivialt serve :6900 /tftproot --writable\nStarting TFTP Server on \":6900\", serving \"/tftproot\"\nRead Request from 127.0.0.1:61877 for \"ubuntu-16.04-server-amd64.iso\"\nWrite Request from 127.0.0.1:51205 for \"ubuntu-16.04-server-amd64.iso\"\n\n```\n\nDownloading a file:\n```\n# trivialt get --help\nNAME:\n   trivialt get - Download file from a server.\n\nUSAGE:\n   trivialt get [command options] [server:port] [file]\n\nOPTIONS:\n   --blksize, -b \"512\"      Number of data bytes to send per-packet.\n   --windowsize, -w \"1\"     Number of packets to send before requiring an acknowledgement.\n   --timeout, -t \"10\"       Number of seconds to wait before terminating a stalled connection.\n   --tsize                  Enable the transfer size option. (default)\n   --retransmit, -r \"10\"    Maximum number of back-to-back lost packets before terminating the connection.\n   --netascii               Enable netascii transfer mode.\n   --binary, --octet, -i    Enable binary transfer mode. (default)\n   --quiet, -q              Don't display progress.\n   --output, -o             Sets the output location to write the file. If not specified the\n                            file will be written in the current directory.\n                            Specifying \"-\" will write the file to stdout. (\"-\" implies \"--quiet\")\n```\n\n```\n# trivialt get localhost:6900 ubuntu-16.04-server-amd64.iso\nubuntu-16.04-server-amd64.iso:\n 655.00 MB / 655.00 MB [=====================================================] 100.00% 16.76 MB/s39s\n```\n\nUploading a file:\n```\n# trivialt get --help\nNAME:\n   trivialt get - Download file from a server.\n\nUSAGE:\n   trivialt get [command options] [server:port] [file]\n\nOPTIONS:\n   --blksize, -b \"512\"      Number of data bytes to send per-packet.\n   --windowsize, -w \"1\"     Number of packets to send before requiring an acknowledgement.\n   --timeout, -t \"10\"       Number of seconds to wait before terminating a stalled connection.\n   --tsize                  Enable the transfer size option. (default)\n   --retransmit, -r \"10\"\tMaximum number of back-to-back lost packets before terminating the connection.\n   --netascii               Enable netascii transfer mode.\n   --binary, --octet, -i\tEnable binary transfer mode. (default)\n   --quiet, -q              Don't display progress.\n   --output, -o             Sets the output location to write the file. If not specified the\n                            file will be written in the current directory.\n                            Specifying \"-\" will write the file to stdout. (\"-\" implies \"--quiet\")\n```\n\n```\n# trivialt put localhost:6900 ubuntu-16.04-server-amd64.iso --blksize 1468 --windowsize 16\nubuntu-16.04-server-amd64.iso:\n 655.00 MB / 655.00 MB [=====================================================] 100.00% 178.41 MB/s3s\n```\n\n## API\n\ntrivialt's API was inspired by Go's well-known net/http API. If you can write a net/http handler or middleware, you should have no problem doing the same for trivialt.\n\n### Configuration Functions\n\nOne area that is noticeably different from net/http is the configuration of clients and servers. trivialt uses \"configuration functions\" rather than the direct modification of the\nClient/Server struct or a configuration struct passed into the factory functions.\n\nA few explanations of this pattern:\n* [Self-referential functions and the design of options](http://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html) by Rob Pike\n* [Functional options for friendly APIs](https://www.youtube.com/watch?v=24lFtGHWxAQ) by Dave Cheney [video]\n\nIf this sounds complicated, don't worry, the public API is quiet simple. The `NewClient` and `NewServer` functions take zero or more configuration functions.\n\nWant all defaults? Don't pass anything.\n\nWant a Client configured for blocksize 9000 and windowsize 16? Pass in `ClientBlocksize(9000)` and `ClientWindowsize(16)`.\n\n``` go\n// Default Client\ntrivialt.NewClient()\n\n// Client with blocksize 9000, windowsize 16\ntrivialt.NewClient(trivialt.ClientBlocksize(9000), trivialt.ClientWindowsize(16))\n\n// Configuring with a slice of options\nopts := []trivialt.ClientOpt{\n    trivialt.ClientMode(trivialt.ModeOctet),\n    trivialt.ClientBlocksize(9000),\n    trivialt.ClientWindowsize(16),\n    trivialt.ClientTimeout(1),\n    trivialt.ClientTransferSize(true),\n    trivialt.ClientRetransmit(3),\n}\n\ntrivialt.NewClient(opts...)\n```\n\n### Examples\n\n#### Read File From Server, Print to stdout\n\n``` go\nclient := trivialt.NewClient()\nresp, err := client.Get(\"myftp.local/myfile\")\nif err != nil {\n    log.Fatalln(err)\n}\n\nerr := io.Copy(os.Stdout, resp)\nif err != nil {\n    log.Fatalln(err)\n}\n```\n\n#### Write File to Server\n\n``` go\n\nfile, err := os.Open(\"myfile\")\nif err != nil {\n    log.Fatalln(err)\n}\ndefer file.Close()\n\n// Get the file info se we can send size (not required)\nfileInfo, err := file.Stat()\nif err != nil {\n    log.Println(\"error getting file size:\", err)\n}\n\nclient := trivialt.NewClient()\nerr := client.Put(\"myftp.local/myfile\", file, fileInfo.Size())\nif err != nil {\n    log.Fatalln(err)\n}\n```\n\n\n#### HTTP Proxy\n\nThis rather contrived example proxies an incoming GET request to GitHub's public API. A more realistic use case might be proxying to PXE boot files on an HTTP server.\n\n``` go\nconst baseURL = \"https://api.github.com/\"\n\nfunc proxyTFTP(w trivialt.ReadRequest) {\n\t// Append the requested path to the baseURL\n\turl := baseURL + w.Name()\n\n\t// Send the HTTP request\n\tresp, err := http.DefaultClient.Get(url)\n\tif err != nil {\n\t\t// This could send more specific errors, but here we'read\n\t\t// choosing to simply send \"file not found\"\" with the error\n\t\t// message from the HTTP client back to the TFTP client.\n\t\tw.WriteError(trivialt.ErrCodeFileNotFound, err.Error())\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\n\t// Copy the body of the response to the TFTP client.\n\tif _, err := io.Copy(w, resp.Body); err != nil {\n\t\tlog.Println(err)\n\t}\n}\n```\n\n\nThis function doesn't itself implement the required `ReadHandler` interface, but we can make it a `ReadHandler` with the `ReadHandlerFunc` adapter (much like `http.HandlerFunc`).\n\n``` go\nreadHandler := trivialt.ReadHandlerFunc(proxyTFTP)\n\nserver.ReadHandler(readHandler)\n\nserver.ListenAndServe()\n```\n\n```\n# trivialt get localhost:6900 repos/golang/go -o - | jq\n{\n  \"id\": 23096959,\n  \"name\": \"go\",\n  \"full_name\": \"golang/go\",\n  ...\n}\n```\n\nFull example in [examples/httpproxy/httpproxy.go](https://github.com/vcabbage/trivialt/blob/master/examples/httpproxy/httpproxy.go).\n\n#### Save Files to Database\n\nHere `tftpDB` implements the `WriteHandler` interface directly.\n\n``` go\n// tftpDB embeds a *sql.DB and implements the trivialt.ReadHandler interface.\ntype tftpDB struct {\n\t*sql.DB\n}\n\nfunc (db *tftpDB) ReceiveTFTP(w trivialt.WriteRequest) {\n\t// Read the data from the client into memory\n\tdata, err := ioutil.ReadAll(w)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\n\t// Insert the IP address of the client and the data into the database\n\tres, err := db.Exec(\"INSERT INTO tftplogs (ip, log) VALUES (?, ?)\", w.Addr().IP.String(), string(data))\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\n\t// Log a message with the details\n\tid, _ := res.LastInsertId()\n\tlog.Printf(\"Inserted %d bytes of data from %s. (ID=%d)\", len(data), w.Addr().IP, id)\n}\n```\n\n```\n# go run examples/database/database.go\n2016/04/30 11:20:27 Inserted 32 bytes of data from 127.0.0.1. (ID=13)\n```\n\nFull example including checking the size before accepting the request in [examples/database/database.go](https://github.com/vcabbage/trivialt/blob/master/examples/database/database.go).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvcabbage%2Ftrivialt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvcabbage%2Ftrivialt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvcabbage%2Ftrivialt/lists"}