{"id":18952394,"url":"https://github.com/tailscale/tailsql","last_synced_at":"2025-10-06T03:11:57.114Z","repository":{"id":187790939,"uuid":"676772256","full_name":"tailscale/tailsql","owner":"tailscale","description":"A SQL playground service over Tailscale.","archived":false,"fork":false,"pushed_at":"2025-08-27T00:21:49.000Z","size":447,"stargazers_count":90,"open_issues_count":0,"forks_count":8,"subscribers_count":19,"default_branch":"main","last_synced_at":"2025-09-29T23:56:04.462Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tailscale.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,"zenodo":null}},"created_at":"2023-08-10T01:33:42.000Z","updated_at":"2025-09-27T10:10:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"39d68f4d-7cb2-421e-9b20-bd692a92f401","html_url":"https://github.com/tailscale/tailsql","commit_stats":null,"previous_names":["tailscale/tailsql"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tailscale/tailsql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailscale%2Ftailsql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailscale%2Ftailsql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailscale%2Ftailsql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailscale%2Ftailsql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tailscale","download_url":"https://codeload.github.com/tailscale/tailsql/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailscale%2Ftailsql/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278551853,"owners_count":26005459,"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","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"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":[],"created_at":"2024-11-08T13:33:07.498Z","updated_at":"2025-10-06T03:11:57.106Z","avatar_url":"https://github.com/tailscale.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TailSQL\n\n[![GoDoc](https://img.shields.io/static/v1?label=godoc\u0026message=reference\u0026color=peru)](https://pkg.go.dev/github.com/tailscale/tailsql)\n[![CI](https://github.com/tailscale/tailsql/actions/workflows/go-presubmit.yml/badge.svg?event=push\u0026branch=main)](https://github.com/tailscale/tailsql/actions/workflows/go-presubmit.yml)\n\n\u003e [!NOTE]\n\u003e Please file issues in our [main open-source repository](https://github.com/tailscale/tailscale/issues).\n\nTailSQL is a self-contained SQL playground service that runs on [Tailscale](https://tailscale.com).\nIt permits users to query SQL databases from a basic web-based UI, with support for any database\nthat can plug in to the Go [`database/sql`](https://godoc.org/database/sql) package.\n\n## Running Locally\n\nRun the commands below from a checkout of https://github.com/tailscale/tailsql.\n\nTo run locally, you will need a SQLite database to serve data from. If you do\nnot already have one, you can create one using the test data for this package:\n\n```shell\n# Creates test.db in the current working directory.\nsqlite3 test.db -init ./server/tailsql/testdata/init.sql .quit\n```\n\nNow build the `tailsql` tool, and create a HuJSON (JWCC) configuration file for it:\n\n```shell\ngo build ./cmd/tailsql\n\n# The --init-config flag generates a stub config pointing to \"test.db\".\n./tailsql --init-config demo.conf\n```\n\nFeel free to edit this configuration file to suit your tastes. The file encodes\nan [Options](./server/tailsql/options.go#L27) value. Once you are satisfied, run:\n\n```shell\n# The --local flag starts an HTTP server on localhost.\n./tailsql --local 8080 --config demo.conf\n```\n\nThis starts up the server on localhost. Visit the UI at http://localhost:8080,\nor call it from the command-line using `curl`:\n\n```shell\n# Note that you must provide a Sec-Tailsql header with API calls.\n\n# Fetch output as comma-separated values.\ncurl -s -H 'sec-tailsql: 1' http://localhost:8080/csv --url-query 'q=select * from users'\n\n# Fetch output as JSON objects.\ncurl -s -H 'sec-tailsql: 1' http://localhost:8080/json --url-query 'q=select location, count(*) n from users where location is not null group by location order by n desc'\n\n# Check the query log.\ncurl -s -H 'sec-tailsql: 1' http://localhost:8080/json --url-query 'q=select * from query_log' --url-query src=self\n```\n\n## Running on Tailscale\n\nTo run as a Tailscale node, make sure the `\"hostname\"` field is set in the\nconfiguration file, then run `tailsql` without the `--local` flag.\n\nNote that the first time you start up a node, you may need to provide a key to\nauthorize your node on the tailnet, e.g.:\n\n```shell\n# Get a key from https://login.tailscale.com/admin/settings/keys.\n# The first time you start the node, provide the key via the TS_AUTHKEY environment.\nTS_AUTHKEY=tskey-XXXX ./tailsql --config demo.conf\n\n# Note: Omit --local to start on Tailscale.\n```\n\nSubsequent runs can omit the key.\n\n## Configuration and Extension\n\nThe command-line tool in `cmd/tailsql` implements a standalone SQL playground server with some default options. The [tailsql][tailsql] package is designed to work as a library, however, and for specific use cases you will probably want to build your own server binary. It is also possible to run a TailSQL server \"inside\" another process, using [tsnet][tsnet]. The example binary includes code you can crib from to suit your own needs.\n\nThe basic workflow to set up a new TailSQL server is:\n\n1. Create the server with your desired [`tailsql.Options`][options]:\n\n    ```go\n    tsql, err := tailsql.NewServer(tailsql.Options{\n       // ...\n    })\n    ```\n\n2. Serve the mux via HTTP:\n\n    ```go\n    http.ListenAndServe(\"localhost:8080\", tsql.NewMux())\n    ```\n\nNote that the server does not do any authentication or encryption of its own. If you want to share the playground beyond your own local machine, you will need to provide appropriate access controls. One easy way to do this is using Tailscale (which handles encryption, access control, and TLS), but if you prefer you can handle those details separately by providing your own implementation of the [`tailsql.LocalClient` interface][lcintf] and setting the [`Authorize` option][authopt].\n\n### Database Drivers\n\nBy default, only SQLite databases are supported (via https://modernc.org/sqlite). Any driver compatible with the [database/sql][dbsql] package should work, however. To add drivers, add additional imports to the main package (`tailsql.go`) and recompile the program.\n\nAdding imports:\n\n```go\nimport (\n  // ...\n\n  // If you want to support other source types with this tool, you will need\n  // to import other database drivers below.\n\n  // SQLite driver for database/sql.\n  _ \"modernc.org/sqlite\"\n\n  // PostgreSQL driver for database/sql.\n  _ \"github.com/lib/pq\"\n)\n```\n\nRebuilding the program:\n\n```shell\ngo build ./cmd/tailsql\n```\n\nTo configure a database using this driver, populate the `Driver` field of the `DBSpec` in the options:\n\n```go\nopts := tailsql.Options{\n   Sources: []tailsql.DBSpec{{\n     Source: \"info\",         // used to select this source in queries (src=info)\n     Label:  \"Information\",  // a human-readable label, shown in the source picker\n     Driver: \"postgres\",     // must be a driver registered with database/sql\n\n     URL: connectionString,  // the connection string for the database\n   }},\n}\n```\n\nAny number of sources can be configured this way. It is also possible to add new data sources dynamically at runtime using the `SetDB` and `SetSource` methods of the server. It is _not_ currently possible to remove data sources once added, however.\n\n### Tailscale Integration\n\nThe `Hostname`, `StateDir`, and `ServeHTTPS` options are not interpreted directly by the library, but are provided to make it easier to connect a TailSQL server to [tsnet][tsnet]. The `cmd/tailsql` program shows how these can be used to run the server on a Tailscale node, either with or without TLS support.\n\n### Query Logging\n\nThe `LocalState` option permits you to enable logging of successful queries in a separate SQLite database maintained by TailSQL itself. If  this option is set, the server will use the specified database to record each query using [`state-schema.sql`][stschema]. If this option is not set, queries are logged only as text (see the `Logf` option).\n\nIn addition, if the `LocalSource` option is set, a read-only view of the the query log database will be included in the list of available data sources, so users can query the log directly in the playground:\n\n```sql\n-- List the five most-recent successful queries.\nselect * from query_log order by timestamp desc limit 5;\n```\n\n### Static Links\n\nThe playground UI is defined in [ui.tmpl][uitmpl], and includes an optional section for static links. These are populated from the `UILinks` option. This is a good place to put links to documentation, for example:\n\n```go\nopts := tailsql.Options{\n   UILinks: []tailsql.UILink{\n     {\n        Anchor: \"Blog\",\n        URL:    \"https://tailscale.com/blog\",\n     },\n     {\n        Anchor: \"Repo\",\n        URL:    \"https://github.com/tailscale/tailsql\",\n     },\n   },\n}\n```\n\n### Authorization\n\nBy default, the server does not do any authorization. The `LocalClient` option allows you to plug the server in to Tailscale: If this option is set, it is used to resolve callers and only logged-in users will be permitted to make queries. (You could theoretically also implement this interface without Tailscale).\n\nTo further customize authorization, you can provide a callback via the `Authorize` option. The [authorizer][authz] package provides some pre-defined implementations, or you can roll your own. This is useful if you want to expose multiple data sources, some of which have more restrictive access policies.\n\n### UI Rewrite Rules\n\nThe server renders column values for the UI as plain strings, using some simple built-in rules for common data types. The `UIRewriteRules` option allows you to extend these rules with custom behaviour. The [uirules][uirules] package provides some pre-defined implementations, or you can roll your own.\n\nRewrite rules are applied in the order they are listed in the options. As each value is rendered, the rules are checked: The first rule that **matches** the column value is **applied** to that value, and the result replaces the default.\n\nFor example:\n\n```go\nblotSecrets := tailsql.UIRewriteRule{\n   // If the column name contains \"password\" or \"passwd\" ...\n   Column: regexp.MustCompile(`(?i)passw(or)?d`),\n\n   // Replace the input text with a redaction marker.\n   Apply: func(column, input string, match []string) any {\n      return \"\u003credacted-password\u003e\"\n   },\n}\n```\n\nOnly the first matching rule is applied; subsequent rules are skipped.\n\n\n\u003c!-- references --\u003e\n[authopt]: https://godoc.org/github.com/tailscale/tailsql/server/tailsql#Options.Authorize\n[authz]: https://godoc.org/github.com/tailscale/tailsql/authorizer\n[dbsql]: https://godoc.org/database/sql\n[lcintf]: https://godoc.org/github.com/tailscale/tailsql/server/tailsql#LocalClient\n[options]: https://godoc.org/github.com/tailscale/tailsql/server/tailsql#Options\n[stschema]: ./server/tailsql/state-schema.sql\n[tailsql]: https://godoc.org/github.com/tailscale/tailsql/server/tailsql\n[tsnet]: https://godoc.org/tailscale.com/tsnet\n[uirules]: https://godoc.org/github.com/tailscale/tailsql/uirules\n[uitmpl]: ./server/tailsql/ui.tmpl\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftailscale%2Ftailsql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftailscale%2Ftailsql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftailscale%2Ftailsql/lists"}