Skip to content

v0.0.5

Latest

Choose a tag to compare

@khj809 khj809 released this 03 Oct 12:13
· 102 commits to main since this release
b381268

🙌 New contributors

🔧 Fixes

Each closes a measured divergence from the vendor's real API. Those that change what a source answers are marked in bold.

Amazon S3

  • Every answer carries real's x-amz-request-id and x-amz-id-2, and an error body repeats them. A method the router does not serve answers what real answers for that method and sub-resource selector, the 405 naming BUCKET, OBJECT, SERVICE or the selector's type, the CORS 400/403 for an OPTIONS, rather than one shared JSON 405. A write is NotImplemented (501). (#319)
  • A refusal names what real names it with, in real's order, and a key in a bucket that does not exist or that the caller cannot see is NoSuchBucket. (#361)
  • Signature Version 2, SigV4 and SigV4a are verified, in the header and in the query, and each fault is refused with real's code, message and members. (#361)
  • A credential scope has to name us-east-1, the region Backlot presents; a client signing for another region gets real's AuthorizationHeaderMalformed, where Backlot used to read the region back out of the scope and serve any. An unsigned request is the anonymous caller's and sees no bucket. (#361)
  • A bucket's and an object's sub-resources answer as real answers an unconfigured one, ?versions is ListObjectVersions, ListBuckets pages and refuses its four parameters as real does, and GetObject reads partNumber, versionId=null and x-amz-checksum-mode. (#361)
  • Four writes are checked before the 501, so a malformed PutObject, DeleteObjects, POST ?restore or PUT ?encryption is refused for what real refuses it for, with all ten checksum algorithms computed. A HEAD is framed the way real frames it. (#361)

Google

  • Gmail's message tree is real's: a message with an attachment is multipart/mixed over a multipart/alternative, every part carries headers, format=raw is built from the same tree, and format=metadata serves mimeType and headers alone. attachments.get serves {size, data}, labels.list serves fifteen labels in the measured order without counts, an empty list is {"resultSizeEstimate": 0}, threads.get has no snippet, and a snippet escapes < and >. (#400)
  • Gmail's threads.get refuses a reply's message id with the 404 a well-formed unknown id gets, rather than serving a one-message thread. (#395)
  • A Gmail attachment read on a missing message or attachment id is real's 400 Invalid attachment token, not a 404. (#394)
  • Drive and Sheets parse every repeat of a typed query parameter and refuse all the failures in one 400 with a details entry each, after the credential and before the lookup. Drive's pageSize and pageToken are checked as real checks them, a blank fields answers {}, and files.export refuses a format the file's type does not export to and serves one it does under the mimeType exactly as sent. (#338)
  • A repeated query parameter is read from the end real reads it from: the first for Drive's fields, q, pageSize, pageToken, orderBy and export mimeType and for Sheets' fields and prettyPrint, the last for the Sheets typed parameters. prettyPrint compacts a Sheets answer at false and 0 only. (#336)
  • An anonymous POST is 401 UNAUTHENTICATED on every family, where a GET on Drive or Sheets stays the 403 unregistered caller. (#316)
  • commentsViewMode on the Docs, Slides and Sheets get routes is an acknowledged gap. (#359)

Notion

  • GET /v1/comments refuses a block_id it cannot use the way real does: absent, empty or not a uuid is a 400 validation_error, a uuid naming nothing visible a 404, a database's id a 403. A visible block's id answers the empty list, and backlot mcp's list_comments now requires block_id. (#398)
  • A refusal carries request_id and x-notion-request-id, and the 401 says which credential failed: a header that is not exactly Bearer <token> is refused for its format, a token that does not resolve as API token is invalid.. (#328)
  • A URL Notion does not publish is real's 400, and an operation it publishes that Backlot does not serve answers the credential first. One trailing slash is dropped and a HEAD is the GET without its body. (#328)

Atlassian

  • A HEAD is the GET with the body left off, and an OPTIONS is answered per product and per caller: Jira's 200 with its measured Allow, Confluence's 404, its search answering by Accept. (#330)
  • A path neither product serves gets each product's own answer: Jira's RFC 7807 No endpoint …, Confluence's JAX-RS 404, and the 401 for an operation the vendor publishes that Backlot does not serve. Outside the API mounts the site answers as its web app does, and a trailing slash or a run of slashes is ignored as the gateway ignores it. (#330)
  • Every answer carries the headers real sends beside the body, atl-request-id, atl-traceid, x-arequestid, x-confluence-request-time, the deprecation trio and Jira's burst quota among them. (#330)
  • Confluence's listings read limit and start with each route's own bounds, child/page, child/comment and label page rather than serving the collection, and every page answers real's _links, the CQL search's cursor included. (#320)
  • Jira's search/jql refuses a maxResults outside 1–5000, a body field it cannot coerce and an unknown body field. A negative maxResults used to serve the whole visible collection. (#286)
  • setContextDefaultValues is an acknowledged gap, with the early-access 403 the site answers recorded beside it. (#368, #399)

GitHub

  • A request is refused once the caller's rate-limit window is spent, real's 403 with used pinned at limit, for a caller with no credential and for a token alike, where Backlot used to let every request through. A suite sending more than ten code searches a minute now meets that refusal; BACKLOT_GITHUB_ENFORCE_RATE_LIMITS=false turns it off. (#317)
  • A trailing slash means what each route makes of it: a ref keeps it and is refused, contents redirects one slash at a time with real's Location encoding, and readme/{dir} serves that directory's README. (#326)
  • /rate_limit answers resources before rate, the two search windows measure a minute, and a caller with no credential has no code_search window of its own. A credential that does not resolve gets its 401 with no rate-limit headers and no count. (#289)
  • A path no route matches carries no version echo, and the x-ratelimit-* headers only for a caller that sent no credential. (#288)
  • relates_to, external installation properties and step logs are acknowledged gaps, and eighteen operations GitHub no longer publishes leave the baseline. (#360)

Slack

  • A file's user is served as a Slack id, minted from the address the corpus writes, as reactions[].users and edited.user already are. The corpus schema now types files[].user as an address, so a corpus carrying "user": "U01" on a file stops at import. (#291)
  • A deactivated member drops the ten fields real never carries for one, real_name, is_admin and tz among them, rather than serving defaults. (#293)
  • has_2fa is served to an admin caller on every active person and to a caller on their own member, and never on a bot. (#293, #344)

Linear

  • AgentSession.codingHarness and Query.dependencyPackageMetadata are acknowledged gaps. (#358)

Tooling, packaging and CI

  • xxhash and cryptography are base dependencies, for S3's checksums and SigV4a. (#361)
  • CONTRIBUTING's pull request checklist asks for ruff check . && ruff format --check . beside pytest. (#396)
  • The linear example tracks @linear/sdk 96.0.0; ruff 0.16.9, fastmcp 4.0.9, google-auth 2.58.1 and the actions group. (#298, #299, #300, #355, #356, #357)

Full changelog: v0.0.4...v0.0.5