I believe you can enforce attestation using MDM and a custom app as far as I'm aware. But for general people using Safari or whatever, you can't request any kind of device attestation.
5,481 karma · joined April 11, 2016
https://austinseipp.com
I believe you can enforce attestation using MDM and a custom app as far as I'm aware. But for general people using Safari or whatever, you can't request any kind of device attestation.
You could accomplish something fairly similar to this for SSH keys actually (an encrypted sync layer + deriving master keys from a local trusted source), but I don't think anyone has done this to the same level of polish.
[1] https://docs.nvidia.com/jetson/archives/r36.3/DeveloperGuide...
I'm not done with the paper yet, but the basics are in fact written on page one.
It's mostly just a matter of having a defined scope. They could of course say "You can only attack this one exact thing" that makes them look good, but this is true of many things.
Defining the threat model is standard in the infosec auditing/pentest world, FWIW.
> If Mullvad dictated how to do things or imposed limits on the reach of the testing, the results are worthless anyway
That's only true if your threat model is "literally every possible thing that could ever happen", which is so broad to be meaningless and impossible to test anyway.
Computer programmers also do not typically design their programs under the assumption that someone stuffed newspaper between their CPU and heatsink and it caught on fire. They work on the assumption the computer is not on fire.
Of course this does not mean their models will necessarily be proportionally better, nor does it mean Google won't buy GPUs for other reasons (like providing them to customers on Google Cloud.)
Note that it's only on one 1.6GHz core, and still pretty anemic otherwise (pi form factor, it's to be expected.) So something "deskop grade" with all the nice extensions and other goodies is probably still a ways off. Maybe next year; we'll have to see -- but lots of useful extensions continue to be ratified today, so it may still be a while before things "cool off."
It supports Mercurial-like "revsets", so it is easy and flexible to query the commit graph for all kinds of information, in a user-controlled way.
Features like 'sl absorb' can do git 'fixup commits' automatically.
It supports a notion of changeset evolution, so if you have `A -> B -> C` and `A` gets merged into main, it will automatically rebase `B -> C` on top (among other things).
"isl", the "interactive sapling" GUI, is absolutely stellar and I don't think there's a faster way to do tasks like 'rebase X onto Y' or "reorder commits in stack Y" than that, even with Jujutsu.
It comes with integration for a tool named ReviewStack so you can do stacked code reviews while working on GitHub.
There is an undo command to undo your mistakes.
And, while I'm not sure this is in the Git backend today, many algorithms like getting the history of a file are O(N) in terms of file history as to the history of the whole repo (e.g. try `git log file` in gecko-dev or Linux and it is slow.)
Many of these advantages (not all) are shared with Jujutsu, to be clear.
There is git-branchless, which is kind of like this[1][2] and is something like a half-way point between Git and Jujutsu/Sapling. However, it needs to be stated that git branchless does NOT just "wrap" existing things or alias them. It has significant engineering to add features, using a lot of code, because you can't "just" add things like revsets or 'undo' using wrappers or aliases, at least not in an efficient way.
Some examples from Jujutsu are that:
A) jj rebase is lightning fast compared to git rebase, because it works in memory. Git rebase touches disk for all operations, so it becomes slower the larger the repo, the more history, the bigger the files etc you have. You may spend significant IOPS moving large-ish files around, flushing the index to disk, and so forth; and you also cannot do things like rebase inside a bare repository (imagine you want a handy "Rebase branch on main" button in your GUI -- you need to have the whole working copy to do that!) jj rebase works entirely in memory so it never touches the disk for any files in the commits themselves. This is called "git move" in git-branchless, but requires significant code. Git itself is trying to solve this with a new "git replay" command, but it is extremely new and has some current limitations compared to, say, Jujutsu, IIRC. (But the fact they have to add a whole new command with significant code is a good example of how the algorithmic differences matter and can't be papered over.)
B) Revsets are a non-trivial addition to make the commit graph "queryable", but they add open ended features in a way that can't easily be recreated without them, because they are composable. For example, "list all commits authored by me that are not merged into main that can be rebased on main" is written as `all:mutable() & mine()`, but to recreate this level of flexibility you would need a dozen one-off features added to something like 'git log' -- but these revsets can work with many more commands than log! You can use the above revset to automatically rebase all your open branches at once for example, which is not possible in Git without shell scripting (what about Windows users?) and duct-taping stuff. But then where's the reuse?
C) Jujutsu has a notion of first-class conflicts, which can only be vaguely approximated with something like `git rerere` and `git rebase --update-refs`, but this isn't quite the same because for example you need to know details like purging the rerere cache if you get a conflict resolution wrong and want to undo it and start over, not to mention you can still easily screw things up mid-way with `git rebase` in a form that requires you to start over. These "catches" do not apply to Jujutsu and I have no idea where you would even begin if you wanted to add a feature like this.
So, the details matter a lot in practice for users, and delivering them a good experience is something that I think is way more difficult than just a bunch of opinionated commands. To be clear, if you like opinionated aliases and wrappers, that's OK! But there's a lot more underneath the surface if you scratch at it a bit.
[1] https://github.com/arxanas/git-branchless
[2] The main developer of git-branchless also is a Jujutsu contributor, so these ideas and the implementations aren't totally a coincidence!
Both of them are heavily Mercurial inspired; Sapling is a heavily evolved fork of Mercurial, while Jujutsu was strongly inspired by many of its concepts but shares no code (the two main developers, Yuya Nishihara and Martin von Zweigbergk, are former heavy hitting Mercurial developers.) They support features like revsets, filesets, have a notion of "changeset evolution" built into their internal models, and more.
These features actually make it possible to do things in an "open ended way" that would be difficult or not-possible to do in Git; something like finding all commits with 'description(foo) & author(aseipp)' for instance is not something I'm sure you can do in Git in a one-off way, as a counterexample to the "just run this command in Git to do that!" argument.
Both of them have a significantly simpler conceptual model because they replace concepts like "stashes" or "the staging area" with commits. To be clear: you can do all the things you like with Git, but with less conceptual overhead. There are fewer "nouns", and consequently fewer "verbs" you need to use and memorize.
They also don't tend to muddle a bunch of stuff together under one name; rebase is just rebase, not "rebase and edit commit message and reorder and throw away commits and merge commits and ..." like in Git. Or consider `git revert` and the dozen options that make it work on this-or-that. This and the previous point mean their UX is also much smaller than Git, a bit more clean and well separated, etc.
Jujutsu has an even further simplified model because it doesn't even have a concept of things like "the working copy." Everything is a commit. This has a large list of downstream consequences on things like performance (jj rebase is vastly more efficient than git rebase), internal design choices, etc but the takeaway is that you have an even simpler cognitive model. The flipside, I think, is that Jujutsu might take slightly longer to "internalize."
Both of them have the ability to expose the `.git` directory next to their own VCS directories (.sl and .jj) so your existing tools like showing status lines in the editor gutter can work. Jujutsu calls this "colocation", and Sapling calls it "dotgit" mode. Sapling's "dotgit" mode is still experimental; Jujutsu's colocation mode is well supported, and likely to become the default in the near future.
Both of them have 'undo' commands. Imagine that! A command that can undo your mistakes! Users are almost always universally filled with joy when they discover this feature.
Both make workflows like stacking or rebasing a lot easier, conceptually, so you will probably be happier with either. Sapling has ReviewStack so you can do stacked reviews a bit more clearly on GitHub. For Jujutsu we don't integrate with ReviewStack, or Reviewable/Graphite (yet), so you are still at the mercy of Github's review tools, which are a bit weak when it comes to interdiffs/stacked PRs.
Sapling has a command called "interactive sapling" or "isl" which is a web-browser based GUI that you can use to drag and drop, reorganize, split, restack commits with ease. isl is an incredible tool, it is easy to use even for newcomers, and I consider it a major advantage over Jujutsu!
Jujutsu has the notion of "first class conflicts" which make most forms of conflict resolution far easier. It's a bit difficult to explain but think of `git rerere` combined with `git rebase --update-refs`, but on steroids. I consider this to be a major advantage over Sapling.
Sapling is the VCS of choice at Meta, and they are working on making the scalable server-side components available too. Jujutsu is designated to (hopefully) become the true successor to 'fig' at Google, which I suspect you're familiar with. Maybe this matters to you as a former Googler, but maybe not. (Note: I do not and have never worked at Google, but Martin does and is the leader of this effort.)
Jujutsu actually exposes a set of APIs so you could in theory use it with other backends; that is how the Piper/CitC integration at Google works, and people have floated ideas like a Mercurial backend, for instance. In theory it is extensible, but this might not matter to you.
Jujutsu is a pure Rust codebase, having been written from scratch, while Sapling has a much longer history; it is a mix of Python and Rust. I personally think this makes it much easier to contribute to Jujutsu, and it was one of the reasons I switched because I personally tend to write patches for tools I use. OTOH, Sapling is much more refined in some ways. It is up to you if you think this matters.
Compatibility-wise, I believe Sapling uses `git` commands underneath to drive a lot of its features, while Jujutsu instead uses libgit2/gitoxide for interop. IMO, libgit2 has caused us and users a few issues long-tail issues that are hard to resolve; things like Git Credential Manager or OpenSSH config support for instance don't work as well with libgit2, which causes annoying bugs. Other features like partial clones don't work (shallow clones do.) You can just use `git` commands to work around this, typically, but I think Sapling might handle some of these quite a bit better.
In short, I think you'll probably be happy with either of them. If you have any questions about Jujutsu at least, you can open them on GitHub, Discord, IRC, etc.
For all intents and purposes, Sapling works very well, I think, and in some places it has a lot more polish, like the "isl" graphical interface, which makes things like rebasing and restacking incredibly intuitive even for new users. (I consider isl to be a significant advantage over Jujutsu, and I say that as a Jujutsu developer!)
[1] Though, Mononoke does support features like exporting Git repositories to remote hosts from the internal model; that's not really what people are looking for in a lot of cases though.
If it means anything, I have been using Git for 15 years and am one of the earliest GitHub users, and I have not regularly used Git for nearly anything, in any job, for over ~1.5 years now. (Disclosure: I am also one of the Jujutsu developers though, so take that for what it is.)
The type 'String' is instead an "owned" type, which means that it is not a reference, and instead a complete value and has a copy of the data. to_string() will create a String (owned value) from a &str (reference) by copying it. This is no different than if you had a global static compile-time string in C and you wanted to modify or update it: you would memcpy the global (statically allocated) string into a local buffer of the appropriate size and then modify it and pass it onward to other things that need it. You would not modify the static string in place.
In short, no, you do not need to_string() every time you want to work with a string. You need it to convert a reference type to an owned type. Rust's type system is just used here to codify the more implicit parts of C or C++'s behavior that you are already familiar with, but the underlying bits and bytes behave as you would expect coming from C++.
> And that bizarre scoping of Person p feels very un-intuitive. How would you work around that if you need to keep using it after show()
You take a reference just like you would in C++. Possibly a mutable reference if you want to modify the thing and then use it afterwords. This is in the article as the "Advanced rust example" at the end, it's right there and not hidden or anything.
It isn't really bizarre honestly; it's a matter of defaults. The difference is that Rust uses move-by-default, not copy-by-default or ref-by-default. Every time you write `x = y` for a given owned type, you are doing a move of `y` and into `x` and thus making `y` invalid.
let g: &str = "Austin"; // statically allocated string
let x: String = g.to_string(); // do a copy
let y: String = x; // no copy, x is moved
Once you internalize this a lot more stuff will make sense, or at least it did for me.I wouldn't be surprised if they saw over 100mil/req/sec globally by now. That's 100 million requests a second that need strong read-your-write consistency and atomicity at global scale. The number of pieces they had to move into place for this to happen is probably quite the engineering tale.
[1] https://aws.amazon.com/blogs/aws/amazon-s3-two-trillion-obje...
And the compiler is not required to lower it to a single instruction. It will if the target architecture is specified appropriately, but there's nothing that says it has to explode if it can't. In fact, by doing it this way, the compiler is actually more free to generate code in a way that's optimal for the architecture in all cases, because all the implementation details are hidden e.g. loads of large constants may be avoided if the compiler is allowed to choose the exact implementation, while using the portable version may tie its hands more depending on how it's feeling on that day. Here's __builtin_popcount working just fine while targeting a ~20yo architecture without native support for SSE4.2; it can generate this code knowing what the proper instructions and schedules are: https://godbolt.org/z/ra7n5T5f3
The moral here is that the primitives are there for you to use. Just use them and save yourself and your would-be code reviewer's time.
That's too easy. What if I give you a 200KiB JSON object with 40+ nested fields that's whitespace stripped and has base64 encoded values? Its "structure" is a red herring. It is not a matter of text or binary. The net result is I still have to use a tool to inspect it, even if that's only something like gron/jq in order to make it actually human readable. But at the end of the day the structure is a concern of the application, I have to evaluate its structure in the context of that application. I don't just look at JSON objects for fun. I do it mostly to debug stuff. I still need the schematic structure of the object to even know what I need to write.
FWIW, I normally use something like grpcurl in order to do curl-like requests/responses to a gRPC endpoint and you can even have it give you the schema for a given service. This has worked quite well IME for almost all my needs, but I accept with this stuff you often have lots of "one-off" cases that you have to cobble stuff together or just get dirty with printf'ing somewhere inside your middleware, etc.
> I would also add the GRPC implementation I used in Javascript (long ago) was not actually checking the types of the field in a lot of cases so rather than being a schema that rejects if some field is not a text field it would just return binary junk. JSON Schema or almost anything else will give you a parsing error instead.
Yes, I totally am with you on this. Many of the implementations just totally suck and JSON is common enough nowadays that you kind of have to at least have something that doesn't completely fall over, if you want to be taken remotely seriously. It's hard to write a good JSON library, but it's definitely harder to write a good full gRPC stack. I 100% have your back on this. I would probably dislike gRPC even more but I'm lucky enough to use it with a "good" toolkit (Rust/Prost.)
> If you’re using JSON Protobufs why would you add this extra complexity - it will mean messaging is just as slow as using JSON. What are the core advantages of GRPC under these conditions?
I mean, if your entire complaint is about text vs binary, not efficiency or correctness, JSON Protobuf seems like it fits your needs. You still get the other benefits of gRPC you'd have anywhere (an honest-to-god schema, better transport efficiency over mandated HTTP/2, some amount of schema-generic middleware, first-class streaming, etc etc.)
FWIW, I don't particularly love gRPC. And while I admit I loathe JSON, I'm mainly pushing back on the notion that JSON has some "schema" or structure. No, it doesn't! Your application has and knows structure. A JSON object is just a big bag of stuff. For all its failings, gRPC having a schema is a matter of it actually putting the correct foot first and admitting that your schema is real, it exists, and most importantly can be written down precisely and checked by tools!
No, it by definition does not, because JSON has no schema. Only your application contains and knows the (expected) structure of the data, but you literally cannot know what structure any random blob of JSON objects will have without a separate schema. When you read a random /docs page telling you "the structure of the resulting JSON object from this request is ...", that's just a schema but written in English instead of code. This has big downstream ramifications.
For example, many APIs make the mistake of parsing JSON and only returning some opaque "Object" type, which you then have to map onto your own domain objects, meaning you actually parse every JSON object twice: once into the opaque structure, and once into your actual application type. This has major efficiency ramifications when you are actually dealing with a lot of JSON. The only way to do better than this is to have a schema in some form -- any form at all, even English prose -- so you can go from the JSON text representation directly into your domain type at parse-time. This is part of the reason why so many JSON libraries in every language tend to have some high level way of declaring a JSON object in the host language, typically as some kind of 'struct' or enum, so that they can automatically derive an actually efficient parsing step and skip intermediate objects. There's just no way around it. JSON doesn't have any schema, and that's part of its appeal, but in practice one always exists somewhere.
You can use protobuf in text-based form too, but from what you said, you're probably screwed anyway if your coworkers are just churning stuff and changing the values of fields and stuff randomly. They're going to change the meaning of JSON fields willy nilly too and there will be nothing to stop you from landing back in step 1.
I will say that the quality of gRPC integrations tends to vary wildly based on language though, which adds debt, you're definitely right about that.
The M4, on all cores, does support the SME extension, which includes a subset of SVE instructions at a wider vector length (512-bits), optimized for throughput. SME instructions are handled by a separate accelerator coprocessor unit attached to each cluster, shared by a number of cores, and don't "exist" in the normal instruction pipeline (where e.g. 256-bit SVE2 instructions would be handled.) This was all true of the proprietary Apple AMX extensions in previous cores as well, as far as I'm aware.
I've spent a lot of time at this point with both toolkits. I use the open source tooling extensively for my own designs. But you tell some grizzled RTL person there's no power analyzer or native SDC support or that UTM was only recently supported in some simulator, and they're going to laugh at you. They've been doing that stuff for 20 years. I know this because I've done it several times (though other people find particular things, like free RTL verification tools, much appreciated.)
I think FOSS/software people paint some very rose-tinted picture in their mind where the mere availability of something for $0 would make it an obvious choice, even at only 10% of the functionality. But that's not how people see it in reality. Many people, including engineers, think of it the other way: if it's so good, why is it $0 and how do I know it will keep being developed? They've made their peace with the fact that the $5000 tool will exist practically for as long as they need, get the job done, and be supported.
> Are you aware of any SQL transpilers that spend so much time transpiling that you get irritated? I'm not.
Again, when you are running a tool on something that returns results in ~millisecond time, it is important the tool does not spoil that. Even 100-200ms is noticeable when you're typing things out. They could have worded it differently, it's probably just typical "A programmer wrote these docs" stuff, so it's just bad copy. A dedicated technical writer would probably do something different.
In particular many people talk a lot about concerns like optimizations across GraphQL plans and how they are expected to behave on underlying tables, but this is something that I think has seen a lot of research in the Datalog realm. And to top it off, even ignoring that, Datalog just feels much more natural to write and read after a bit of practice, I think. (Obviously you need to be in the pure fragment of datalog without recursion, but even then it might be feasible to add those features with termination criteria even if it's just "decrement an internal counter and if it hits zero throw a big error")
What do you think the plans for the Rust implementation will be? That's probably the most likely place I'd use it, as I don't really use Go that much.
I don't really get this kind of complaint in general I'm afraid. Many people can read and write, say, Hangul just fine -- and at the same time we don't expect random English speakers with no familiarity will be able to understand Korean conversations, or any syllabic writing systems in general. Programming language families/classes like logic programming are really no different.
> it's English language syntax made things really easy
That's just called "being familiar with English" more than any inherent property of SQL or English.
> making variables implicit at the call site
What example are you looking at? The NewsData example for instance seems pretty understandable to me. It seems like for any given predicate you can either take the implicit name of the column or you can map it onto a different name e.g. `date: date_num` for the underlying column on gdelt-bq.gdeltv2.gkg.
Really it just seems like a way to make the grammar less complicated; the `name: foo` syntax is their way of expressing 'AS' clauses and `name:` is just a shorthand for `name: name`
> In light of that quote it's also weird that they mention that they are able to run the SQL they compiled to "in interactive time" on a rather large dataset, which they supposedly already could with SQL.
The query in question is run on BigQuery (which IIRC was the original and only target database for Logica), and in that setup you might do a query over 4TB of data but get a response in milliseconds due to partitioning, column compression, parallel aggregation, etc. This is actually really common for many queries. So, in that kind of setup the translation layer needs to be fast so it doesn't spoil the benefit for the end user. I think the statement makes complete sense, tbh. (This also probably explains why they wrote it in Python, so you could use it in Jupyter notebooks hooked up to BigQuery.)
You can bolt on any number of 3rd party features or extensions to get some extra thing, that goes for any tool in the world. The point of something like Datalog is that it can express a similar class of relational programs that SQL can, but with a smaller set of core ideas. "Do more with less."
> I guess, but how can you guarantee correctness with flexibility involved?
How do you guarantee the correctness of anything? How do you know any SQL query you write is correct? Well, as the author, you typically have a good idea. The point of being compositional is that it's easier to stick together arbitrary things defined in Datalog, and have the resulting thing work smoothly.
Going back to the previous example, you can define any two "tables" and then just derive a third "table" from these, using language features that you already use -- to define relationships between rows. Datalog can define relations between rules (tables) and between facts (rows), all with a single syntactic/semantic concept. While SQL can only by default express relations between rows. Therefore, raw SQL is kind of "the bottom half" of Datalog, and to get the upper half you need features like CTEs, VIEWs, etc, and apply them appropriately. You need more concepts to cover both the bottom and top half; Datalog covers them with one concept. Datalog also makes it easy to express things like e.g. queries on graph structures, but again, you don't need extra features like CTEs for this to happen.
There are of course lots of tricky bits (e.g. optimization) but the general idea works very well.
> Could you share a more specific example or scenario where you have seen Datalog/ Logica outperform a modern SQL setup?
Again, Datalog is not about SQL. It's a logic programming language. You need to actually spend time doing logic programming with something like Prolog or Datalog to appreciate the class of things it can do well. It just so happens Datalog is also good for expressing relational programs, which is what you do in SQL.
Most of the times I'm doing logic programming I'm actually writing programs, not database queries. Trying to do things like analyze programs to learn facts about them (Souffle Datalog, "can this function ever call this other function in any circumstance?") or something like a declarative program as a decision procedure. For example, I have a prototype Prolog program sitting around that scans a big code repository, figures out all 3rd party dependencies and their licenses, then tries to work out whether they are compatible.
It's a bit like Lisp, in the sense that it's a core formulation of a set of ideas that you aren't going to magically adopt without doing it yourself a bunch. I could show you a bunch of logic programs, but without experience all the core ideas are going to be lost and the comparison would be meaningless.
For the record, I don't use Logica with SQL, but not because I wouldn't want to. It seems like a good approach. I would use Datalog over SQL happily for my own projects if I could. The reasons I don't use Logica for instance are more technical than anything -- it is a Python library, and I don't use Python.
Datalog is not really a query language, actually. But it is relational, like SQL, so it lets you express relations between "facts" (the rows) inside tables. But it is more general, because it also lets you express relations between tables themselves (e.g. this "table" is built from the relationship between two smaller tables), and it does so without requiring extra special case semantics like VIEWs.
Because of this, it's easy to write small fragments of Datalog programs, and then stick it together with other fragments, without a lot of planning ahead of time, meaning as a language it is very compositional. This is one of the primary reasons why many people are interested in it as a SQL alternative; aside from your typical weird SQL quirks that are avoided with better language design (which are annoying, but not really the big picture.)