From a quick look (on mobile) I can see that the function takes parameters, but you're not using that.
chrismorgan 18 days ago [-]
parsePath(…).query is a URLSearchParams, so those parameters are just strings, and it’s an untagged template string, so it is indeed a trivial SQL injection vector. And the params argument was right there! I don’t understand quite how you make a mistake like that, but it’s extremely worrying. This is fundamental, basic stuff.
The fun aspects of this are that:
(a) I could actually imagine trailbase.js contents that would make it not SQL injection: you could have parsePath(…).query.get(…) return objects with a toString() that escaped SQL. This would raise even more questions, and I was sure it wouldn’t be the case, but it’s possible.
(b) You could make it work safely, converting interpolation into parameters, by using a tagged template string. This could require only a tiny change:
return await query(
sql`SELECT Owner, Aroma, Flavor, Acidity, Sweetness
FROM coffee
ORDER BY vec_distance_L2(
embedding, '[${+aroma}, ${+flavor}, ${+acid}, ${+sweet}]')
LIMIT 100`
);
(You could even make it query`…`, but I think query(sql`…`) is probably wiser. As for the plusses I put in there, that’s to convert from strings to numbers.)
it looks like that the query function has a second parameter with a "params" array.
probably something like this would work then too (didn't test):
return await query(
`SELECT Owner, Aroma, Flavor, Acidity, Sweetness
FROM coffee
ORDER BY vec_distance_L2(
embedding, '[?,?,?,?]')
LIMIT 100`,
[+aroma, +flavor, +acid, +sweet],
);
it is nice because the query string is constant then and a prepared query could be cached..
chrismorgan 18 days ago [-]
So we’re clear, the tagged template string approach gives you a string that uses parameters properly (see the example in the README of that project that does it); it’s perfectly compatible with prepared-statement caching.
And actually on second consideration, you probably can’t use binding parameters here: you’re trying to slot numbers in as JSON values inside a string! Maybe you’d have to write vec_distance_L2(embedding, ?), with parameter JSON.stringify([+aroma, +flavor, +acid, +sweet]).
uwemaurer 18 days ago [-]
you are right, great comment!
trailbase-alt 18 days ago [-]
:hide: thanks for pointing out this embarrassing plunder. No excuse, just feeling silly especially after being so careful throughout the rust base. It's fixed now. Please keep yelling if you find more issues
chrismorgan 17 days ago [-]
Well… a couple of things that could be done better in that example:
That type stinks. Kill the undefined bit by giving it an initial value, then you can skip the `?? []` later too; and replace Array<object> with the actual row type, probably naming it, maybe like this:
type Record = [string, number, number, number, number];
const [data, setData] = useState<Record[]>([]);
const params = new URLSearchParams({ aroma, flavor, acidity, sweetness });
I confess I’m puzzled about this one too, as trailbase.js suggests you know about URLSearchParams, which is what you should use for all typical query string manipulation, parsing and generation.
trailbase 17 days ago [-]
Done. Thanks
klaussilveira 18 days ago [-]
Interesting to see all these Rust and TS server-side projects fumbling on the same mistakes early PHP 4 was crucified for.
bbotond 18 days ago [-]
What would actually be interesting is if you told us what those mistakes were, instead of just vagueposting.
rizky05 18 days ago [-]
[dead]
jagged-chisel 18 days ago [-]
There’s this whole thread about string handling. Does the Rust API into SQLite not support prepared statements?
ansible 18 days ago [-]
You can insert arguments into an SQL statement with the (?1) style syntax. See the example here:
no, but the guy can also look at postgres.js that already implmented alot of the sql`SELECT * FROM ${table_name}` Where there vars are escaped. It ONLY works for postgres, but I'm sure you can make it work with SQLite as well.
Mucking about with SQL strings, to me, is akin to writing your own crypto: Don't. Unless you must (because, say, it doesn't exist yet.) Doing this is asking for security problems later. Trust that the SQLite team (or any other SQL engine) have more experience and have provided a correct interface. And if that interface isn't correct, don't just run off an make your own- contribute a fix so we can keep all the lessons learned together.
noelwelsh 18 days ago [-]
I found the Trailbase documentation did a very poor job of describing what this is. It solely describes Trailbase in terms of differences from Pocketbase and Supabase (and that's even the first entry in the FAQ), which assumes the reader knows what Pocketbase and Supabase are. In contrast, the Pocketbase documentation got to the point very quickly.
romantomjak 18 days ago [-]
I was aware of Supabase, but still was confused as to what this project actually does. The mention of "1 file" and "app server" wasn’t helpful either. Does that mean a single binary? One SQLite file? Does it execute other binaries? I’m not sure. In contrast, when I visited the PocketBase website - it provided a much clearer explanation of its purpose.
dizhn 18 days ago [-]
I thought it was going to be like OpenResty but with rust instead of lua.
aprilfoo 18 days ago [-]
"Application server" is an umbrella term and the documentation doesn't really help identifying what the perimeter is.
Beside, it states that it is so fast that there's no need for cache, and supports SQLite only. So it seems to target only very simple applications, eg. straightforward relational DB <-> Json Rest/HTTP.
trailbase 18 days ago [-]
> So it seems to target only very simple applications, eg. straightforward relational DB <-> Json Rest/HTTP.
Could you expand a bit on what more advanced capabilities you're missing for anything beyond a very simple app? I'm genuinely curious but also see SQLite often undersold
aprilfoo 17 days ago [-]
Processing beyond simple DB data retrieval or io dependent processes can be time consuming and fully justify a cache. Eg, geographic processing that goes beyond geoDB capabilities with millions of rows, eventually involving retrieval of data from third party providers can take long so you'll want to cache results one way or another.
No denying SQLite is very capable. However in my understanding it's designed for being used by a single application, it's not really scalable, and its functionalities are still limited compared to the big DBs. Anyway there's no single fit for all cases, and for core components flexibility is really important.
trailbase 15 days ago [-]
Thanks for expanding.
> retrieval of data from third party providers can take long so you'll want to cache results one way or another.
Agreed. Caching of slow external sources will always be beneficial (at least if you need it more than once :) ). You should use whatever makes the most sense. The argument is more that data from TB is already pretty quick, quick enough that you could even use it as a cache.
> However in my understanding it's designed for being used by a single application,
SQLite by design allows for concurrent writes and parallel reads from any number of processes.
> it's not really scalable,
What do you want to scale to? Postgres is fairly similar in terms of scalability, i.e. master writes + read replicas. Once you're going beyond that scale you get a lot more benefit out of more specialized, problem-oriented solutions.
> and its functionalities are still limited compared to the big DBs.
It full SQL and it's extensible. Depends on what you need. It certainly doesn't have the same rich ecosystem or flexibility of posgres where you can even swap out the storage engine. I was mostly curious, if there's some specific functionality you're missing.
> and for core components flexibility is really important.
Agreed: the right kind of flexibility for your problem.
I'll play devils advocate here: imagine you'd start to deeply depend on one specific postgres plugin and then that plugin get discontinued or you're hitting performance bottlenecks and you're to locked-in to move to a more specialized solution. Don't take this too seriously, I just wanna say that there's a balance.
QuadrupleA 18 days ago [-]
Not entirely clear what it is, sounds a bit like https://redbean.dev/ with more app languages but no universal executable?
Sammi 18 days ago [-]
I love how the comparison articles are so flattering to the projects that this is inspired by and competes with.
They're great, mature and polished projects :). TrailBase has ways to go to get there
kiwicopple 18 days ago [-]
as a supabase team member, I must say that I also found it flattering.
nice work OP - i like this version of tech where we all get along. the project looks great, good luck!
18 days ago [-]
williamoforange 18 days ago [-]
Looks cool, but in what world is any of that 'single-file'? Like yes your examples are built out of single files, several of them in fact. And the sever certainly isn't a single file...
divbzero 18 days ago [-]
People do refer to these standalone apps as “single-file” [1] but I find “single executable” [2] to be less ambiguous.
That's "single-file deployment." I've never heard anyone shorten single-file deployment as just "single-file," until now.
formerly_proven 18 days ago [-]
Java .wars, but native and talking HTTP.
PhilipRoman 18 days ago [-]
I guess "statically linked" just doesn't have the same ring to it. (And even with that, it still suggests using docker...)
trailbase 18 days ago [-]
You folks are spot on. Single file in this case means single binary that is statically liked, i.e. w/o dependencies on shared library allowing you to just copy it from machine to machine similar to e.g. k3s.
Whether to use docker or not is really up to you. It's just a way to deploy other assets, e.g. a static html bundle. More importantly it saves me the trouble of providing windows builds at this current time :)
LoganDark 18 days ago [-]
Pretty sure they mean the server itself is statically linked. It still depends on external files for any configuration / the applications themselves.
atoav 18 days ago [-]
You download a single executable file and execute it and it runs everything needed.
huqedato 18 days ago [-]
What is "application server" ? The documentation fails to address the most crucial aspect of their project: its purpose.
GTP 18 days ago [-]
I'm missing something here. I'm familiar with TomCat, but that's an application server for JVM programs. Here the project specifies which technologies are used to develop it, but which kind of applications will it serve? Are the applications supported by this server using a specific runtime? Will it serve native applications? Sorry for the naive questions, I'm not into web development.
phrotoma 18 days ago [-]
It's a continuation of a trend in which open source projects mimick the functionality of a tool called Firebase that Google bought in 2014 and is now part of Google Cloud (see also supabase and pocketbase). All these projects seem to be sort of like Redis in as much as they stick a datastore behind an API saving users from needing to use SQL and offering them a higher level API instead. And it also seems to do some sort of auth things.
I think it's meant for people who want to build a front end without building a backend.
kiwicopple 18 days ago [-]
> stick a datastore behind an API saving users from needing to use SQL and offering them a higher level API instead
supabase team here. Yes, we offer an auto-generated API (using PostgREST). But users can also connect directly to their Postgres database or through a connection pooler. People have preferences, so we offer options.
snotrockets 18 days ago [-]
I don’t think this was (is? I don’t follow it too closely) the use case for Redis.
phrotoma 17 days ago [-]
You don't think Redis is a datastore with an API?
Sammi 18 days ago [-]
It's kinda like only the backend part of a cms system. Say if you took the backend part of Wordpress/Joomla/Drupal and exposed apis for a user developed frontend.
lavren1974 18 days ago [-]
pocketbase on rust. The demo gives a good impression, if I have time I'll play with your project
fastball 18 days ago [-]
And uses V8 as the JS runtime so you can use modern JS language features (goja in pocketbase is ES5 only).
d1sxeyes 18 days ago [-]
Well yes but how often do you have to write a JS extension for PocketBase that needs to be super performant and use modern JS features?
fastball 18 days ago [-]
All JS I write in 2024 is ES6+ (usually more like ES2022+)
Also, what features do you need to code in JavaScript to extend PocketBase functionality?
fastball 18 days ago [-]
Anything you want to write in JS. After all we are comparing Trailbase to Pocketbase, both of which allow you to write application logic in JS.
d1sxeyes 18 days ago [-]
They do. My point is that for the vanishingly small number of use cases where you need to write application logic in PocketBase, and you need to write it in JavaScript, and you need to use unsupported super modern ES6 features, you might prefer Trailbase, but otherwise PocketBase is more polished at this point.
trailbase 18 days ago [-]
IMHO, this will greatly depend on your use-cases. If you have a rich client app, e.g. mobile or PWA, most of your logic will go into your client by default.
However, there might be some code you want to run on the server in a trusted environment, or use some existing JS library for mobile, or have an otherwise mostly static web app, ... depends
desireco42 18 days ago [-]
I came here to understand what is the use-case of this, thank you!
ripley12 18 days ago [-]
I also found it kind of hard to understand what it's for from the docs. Logging into the demo UI (linked on the GitHub README) helped a lot.
ripley12 18 days ago [-]
This looks quite nice overall and the demo is slick.
It looks like backend APIs need to be written in JS and and then deployed as separate files. Not quite what I want, but I can appreciate why they went that route.
It'd be neat to have a project like this as a Rust crate, where you can write your own APIs in Rust and compile the whole thing to a single file.
tonyhart7 18 days ago [-]
"this as a Rust crate, where you can write your own APIs in Rust and compile the whole thing to a single file"
not quite the same but try loco.rs, for me its great
rcarmo 18 days ago [-]
How did you log in to the demo when the stated credentials in the README don’t work?
Always interesting to me these type of solutions choose SQLite (which is great) and not something like Postgres with Orioledb. With SQLite you have to also use Litestream for S3 backups and this is always something I'm surprised isn't found in these types of solutions.
xixixao 18 days ago [-]
You can no longer have a single executable running, you also need Postgres running.
imtringued 17 days ago [-]
That already exists and is called supabase. If you want to compete, you have to be different.
lovasoa 18 days ago [-]
The project looks great !
But I wonder who the audience is... The website says "serve millions of customers from a tiny box". Who does this appeal to ?
Solo developers who have million of users, need very low latency, yet are happy with a just a SQLite database for their backend ?
ForHackernews 18 days ago [-]
I think the audience for these projects are frontend developers who don't want/need to write much of a backend. They need some kind of data store + a dumb CRUD API to use with their mobile app or javascript SPA.
lovasoa 18 days ago [-]
But aren't these folks already served well by PocketBase ?
And by the time they get a million user, they will probably not be hosting on a single "tiny box" anyway. At what point do you think : okay, I need PocketBase, but ten times faster ?
knowitnone 18 days ago [-]
I don't question people's projects. Creator obviously thinks there was a need and that they could do better than PocketBase. It's the same as asking why BSD when we have Linux. Why Wayland when we have Xorg. Who knows, maybe this will succeed PocketBase. Since when has competition been a bad thing?
xixixao 18 days ago [-]
If you want a reactive backend then this is similar to Convex’s open source backend, which also uses TS for writing backend functions and SQLite for persistence: https://github.com/get-convex/convex-backend
You get a consistent client for React, simple file storage, built-in text search…
icemelt8 18 days ago [-]
its very hard to host it, there is no official docker compose file
threecheese 18 days ago [-]
The docs specifically warn against it as well. A “here be dragons”, at least they are honest.
xixixao 18 days ago [-]
Agree it would be nice to have, there is an open PR with one.
tgdn 18 days ago [-]
The demo credentials return 401 Uncaught Error
jdthedisciple 18 days ago [-]
same here, but you just have to try once or twice more, then it worked for me
trailbase-alt 18 days ago [-]
Sorry, this is folks logging in and changing the password. Should have seen this coming :hide:. Instance gets reset every 60min. Not great but something to improve
voidmain0001 18 days ago [-]
I know naming a project is difficult so please don’t take offence when I write my first thought on reading the name trailbase and thinking “why would a project that wants to lead with speed name itself for moving slowly?” I’m sure they’re thinking of the other use of trail, not meandering or dragging.
trailbase 18 days ago [-]
That's very valid and also why it's funny. Naming is hard. To be honest, I didn't think much about "trailing", certainly trailing in terms of adoption :). I did like the sound of "trail run" on the command line.
Definitely considered a bunch of alternatives. "SecondBase" was fun :)
darrenf 18 days ago [-]
I assumed it was meant to evoke "trailblaze".
tmountain 18 days ago [-]
Same.
ianpurton 18 days ago [-]
I find the "in 1 file" message confusing.
Does it mean sqlite, so just 1 file which is the database?
cess11 18 days ago [-]
It means the application compiles down to one binary.
> This file implements a VFS shim that allows an SQLite database to be appended onto the end of some other file, such as an executable.
(A quick search of their codebase reveals no results for "appendvfs", so I guess not. So single-file was a lie!)
cess11 18 days ago [-]
I'm no rustacean but to me it looks like start() in https://github.com/trailbaseio/trailbase/blob/main/trailbase... consumes a file and returns a database connection. If it were to step into the file and seek out an embedded file system I suspect that function would look different.
notRobot 18 days ago [-]
"single file" terminology in the context of applications does not typically include the DB. It is common for single file executables to use external config files and DB files.
radiator 18 days ago [-]
But then how should redbean describe itself? There you have the executable plus any configuration files or other files you might possible need zipped inside one single file (and if you need a DB, also one more file for the sqlite database next to it, OK).
Retr0id 18 days ago [-]
I'm aware, the same definition was given in the comment I was replying to.
up2isomorphism 14 days ago [-]
It sounds weird to emphasize “rust and SQLite”, where the first one is a language and second is an application. Anyway, SQLite is C.
I think the author means that it stores data on disk in a single file (aka SQLite!)
trailbase 18 days ago [-]
It's a single, statically linked binary to run everything. You don't have to worry about dynamic libraries dependencies or other assets.
Storage on disk is actually a couple of files of data bases, uploaded files, keys, config, ...
17 days ago [-]
icemelt8 18 days ago [-]
The admin UI is not good, text is overlapping everywhere, and want to see how you show relations in the UI, for me this is the first thing I look for in any DB UI
tonyhart7 18 days ago [-]
he uses solid, should choose sveltekit if he want beatifull UI
tinyrichard 18 days ago [-]
At least myself and one other have realized we can add users, 10/10 demo. Only the best test users.
The tech looks cool but I don't understand what problem it solves
Havoc 18 days ago [-]
Demo site crashes with "uncaught error 401"
trailbase-alt 18 days ago [-]
Sorry, this is folks logging in and changing the password. Should have seen this coming :hide:. Instance gets reset every 60min. Not great but something to improve
Havoc 18 days ago [-]
haha that's both hilarious and sad.
Who the hell changes passwords on a demo site?
Onavo 18 days ago [-]
Note the license is similar to AGPL. Don't expect to use it in any production apps, stick with Pocket base for now.
max-privatevoid 18 days ago [-]
[flagged]
trailbase 18 days ago [-]
[flagged]
cess11 18 days ago [-]
[flagged]
chrismorgan 18 days ago [-]
From the text of the license:
> If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License.
> This means that distributing OSL software on ordinary FTP sites, sending patches to ordinary mailing lists, or storing the software in an ordinary version control system, is arguably a violation of the license and would subject you to possible termination of the license. Thus, the Open Software License makes it very difficult to develop software using the ordinary tools of free software development.
trailbase 18 days ago [-]
That sounds concerning. Maybe OSL-3 is the wrong license. I am not a lawyer and spend too much time to admit on researching options before settling on OSL-3. My impression was that OSL-3 is roughly AGPL with a linkage exception to account for both use-cases of using it as a stand-alone service or as a rust library. Maybe a dual license would have been more appropriate? TBH, I'm also not sure I fully grasp: https://www.gnu.org/licenses/license-list.html#OSL
cess11 18 days ago [-]
I mentioned commercial use where you don't modify the software, not sure why you bring that up.
Just put a link to the git repo somewhere in your service, like an about page or whatever, then people can go read the license or make their own copies and so on.
chrismorgan 18 days ago [-]
Sorry, I was just tacking it onto a discussion-of-the-license subthread.
18 days ago [-]
18 days ago [-]
752963e64 18 days ago [-]
[dead]
Rendered at 13:42:19 GMT+0000 (Coordinated Universal Time) with Vercel.
Is the string interpolation straight into the sql from the query string in your getting started docs (https://trailbase.io/getting-started/first-ui-app) safe? It smells a bit.
From a quick look (on mobile) I can see that the function takes parameters, but you're not using that.
The fun aspects of this are that:
(a) I could actually imagine trailbase.js contents that would make it not SQL injection: you could have parsePath(…).query.get(…) return objects with a toString() that escaped SQL. This would raise even more questions, and I was sure it wouldn’t be the case, but it’s possible.
(b) You could make it work safely, converting interpolation into parameters, by using a tagged template string. This could require only a tiny change:
(You could even make it query`…`, but I think query(sql`…`) is probably wiser. As for the plusses I put in there, that’s to convert from strings to numbers.)This is a concept that’s definitely been done seriously. The first search result I found: https://github.com/blakeembrey/sql-template-tag.
probably something like this would work then too (didn't test):
it is nice because the query string is constant then and a prepared query could be cached..And actually on second consideration, you probably can’t use binding parameters here: you’re trying to slot numbers in as JSON values inside a string! Maybe you’d have to write vec_distance_L2(embedding, ?), with parameter JSON.stringify([+aroma, +flavor, +acid, +sweet]).
—⁂—
That type stinks. Kill the undefined bit by giving it an initial value, then you can skip the `?? []` later too; and replace Array<object> with the actual row type, probably naming it, maybe like this: —⁂— No need to construct the query string manually: I confess I’m puzzled about this one too, as trailbase.js suggests you know about URLSearchParams, which is what you should use for all typical query string manipulation, parsing and generation.https://docs.rs/rusqlite/latest/rusqlite/
Mucking about with SQL strings, to me, is akin to writing your own crypto: Don't. Unless you must (because, say, it doesn't exist yet.) Doing this is asking for security problems later. Trust that the SQLite team (or any other SQL engine) have more experience and have provided a correct interface. And if that interface isn't correct, don't just run off an make your own- contribute a fix so we can keep all the lessons learned together.
Beside, it states that it is so fast that there's no need for cache, and supports SQLite only. So it seems to target only very simple applications, eg. straightforward relational DB <-> Json Rest/HTTP.
Could you expand a bit on what more advanced capabilities you're missing for anything beyond a very simple app? I'm genuinely curious but also see SQLite often undersold
No denying SQLite is very capable. However in my understanding it's designed for being used by a single application, it's not really scalable, and its functionalities are still limited compared to the big DBs. Anyway there's no single fit for all cases, and for core components flexibility is really important.
> retrieval of data from third party providers can take long so you'll want to cache results one way or another.
Agreed. Caching of slow external sources will always be beneficial (at least if you need it more than once :) ). You should use whatever makes the most sense. The argument is more that data from TB is already pretty quick, quick enough that you could even use it as a cache.
> However in my understanding it's designed for being used by a single application,
SQLite by design allows for concurrent writes and parallel reads from any number of processes.
> it's not really scalable,
What do you want to scale to? Postgres is fairly similar in terms of scalability, i.e. master writes + read replicas. Once you're going beyond that scale you get a lot more benefit out of more specialized, problem-oriented solutions.
> and its functionalities are still limited compared to the big DBs.
It full SQL and it's extensible. Depends on what you need. It certainly doesn't have the same rich ecosystem or flexibility of posgres where you can even swap out the storage engine. I was mostly curious, if there's some specific functionality you're missing.
> and for core components flexibility is really important.
Agreed: the right kind of flexibility for your problem.
I'll play devils advocate here: imagine you'd start to deeply depend on one specific postgres plugin and then that plugin get discontinued or you're hitting performance bottlenecks and you're to locked-in to move to a more specialized solution. Don't take this too seriously, I just wanna say that there's a balance.
https://trailbase.io/comparison/pocketbase/ https://trailbase.io/comparison/supabase/
nice work OP - i like this version of tech where we all get along. the project looks great, good luck!
[1]: https://learn.microsoft.com/en-us/dotnet/core/deploying/sing...
[2]: https://nodejs.org/api/single-executable-applications.html
Whether to use docker or not is really up to you. It's just a way to deploy other assets, e.g. a static html bundle. More importantly it saves me the trouble of providing windows builds at this current time :)
I think it's meant for people who want to build a front end without building a backend.
supabase team here. Yes, we offer an auto-generated API (using PostgREST). But users can also connect directly to their Postgres database or through a connection pooler. People have preferences, so we offer options.
Also, what features do you need to code in JavaScript to extend PocketBase functionality?
It looks like backend APIs need to be written in JS and and then deployed as separate files. Not quite what I want, but I can appreciate why they went that route.
It'd be neat to have a project like this as a Rust crate, where you can write your own APIs in Rust and compile the whole thing to a single file.
not quite the same but try loco.rs, for me its great
But I wonder who the audience is... The website says "serve millions of customers from a tiny box". Who does this appeal to ?
Solo developers who have million of users, need very low latency, yet are happy with a just a SQLite database for their backend ?
And by the time they get a million user, they will probably not be hosting on a single "tiny box" anyway. At what point do you think : okay, I need PocketBase, but ten times faster ?
Definitely considered a bunch of alternatives. "SecondBase" was fun :)
Does it mean sqlite, so just 1 file which is the database?
> This file implements a VFS shim that allows an SQLite database to be appended onto the end of some other file, such as an executable.
(A quick search of their codebase reveals no results for "appendvfs", so I guess not. So single-file was a lie!)
https://github.com/trailbaseio/trailbase/blob/main/Cargo.loc...
Storage on disk is actually a couple of files of data bases, uploaded files, keys, config, ...
- Lang: Go vs Rust
- JS Runtime: goja (ES5 only) vs V8
Who the hell changes passwords on a demo site?
> If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License.
No wonder FSF criticise it and urges developers not to choose it! https://www.gnu.org/licenses/license-list.html#OSL:
> This means that distributing OSL software on ordinary FTP sites, sending patches to ordinary mailing lists, or storing the software in an ordinary version control system, is arguably a violation of the license and would subject you to possible termination of the license. Thus, the Open Software License makes it very difficult to develop software using the ordinary tools of free software development.
Just put a link to the git repo somewhere in your service, like an about page or whatever, then people can go read the license or make their own copies and so on.