Repository navigation
Conversation
4c73e6a to
591d116
Compare
591d116 to
f780f40
Compare
f780f40 to
4b0da87
Compare
4b0da87 to
f013f26
Compare
f013f26 to
7851679
Compare
7851679 to
c2757eb
Compare
92254ea to
12f6ab3
Compare
12f6ab3 to
37388ff
Compare
0601f58 to
34cdf77
Compare
34cdf77 to
da83ed9
Compare
08d1cc1 to
ff724dd
Compare
eb1d0bf to
568a844
Compare
sigurdm
left a comment
There was a problem hiding this comment.
This could use also an update to ## Publishing Packages in doc/repository-spec-v2.md
0568241 to
f239f47
Compare
sigurdm
left a comment
There was a problem hiding this comment.
I have a proposal for how to communicate the attestation that would simplify both the spec and the server implementation:
The issue with uploading attestations to GCS
Google Cloud Storage's form POST API only allows uploading a single object per request. To upload both the archive and attestation to GCS, the server must generate two separately signed policies.
On pub.dev, signing a policy requires an IAM signBlob RPC. If GET /api/packages/versions/new returns two signed URLs unconditionally, we incur 2x IAM latency (50–200ms) and 2x quota on every publish, even though 99.9% of publishes don't have an attestation. In addition, during finalization the server has no record of whether the client intended to upload an attestation, so a dropped upload could lead to silently publishing without provenance.
Proposal: Send the attestation in the finalize request
An attestation bundle (.sigstore.json) is only 3–10 KB. Unlike the 100 MB archive, which must be uploaded directly to GCS to avoid buffering in App Engine / Cloud Run memory, a 5 KB JSON bundle can be sent directly to the server.
GET /api/packages/versions/newstays completely unchanged. No new fields, no capability negotiation, and only 1 IAM call to sign the archive upload policy.- The client uploads
package.tar.gzto GCS as today. - During finalization, if publishing with an attestation, the client issues a
POSTto<finalize-upload-url>with{ "attestation": <sigstore JSON bundle> }. If publishing without an attestation, it issues aGETas today.
Benefits:
- Zero changes to
versions/new: No extra IAM calls or latency onpub.dev. - No multi-object storage complexity: No second signed URL, no orphan objects in GCS.
- Atomic delivery: The attestation arrives directly with the finalize call, so the server knows unambiguously that this publish includes an attestation.
- Compatibility: If a server doesn't support attestations, it will return
405 Method Not Allowedon thePOST, which the client can report as "The repository does not support publishing with attestations."
3515b19 to
789afc5
Compare
789afc5 to
4a5fea1
Compare
- Adds `--with-attestation <bundle.sigstore.json>` flag to `dart pub publish --from-archive`. - Enforces that a GitHub repository is specified in `pubspec.yaml` (`repository` field) when publishing with an attestation. - In `_publishUsingClient`, uploads the attestation JSON bundle alongside `package.tar.gz` to the hosting cloud storage bucket. - Adds comprehensive unit tests for `--with-attestation` and validation checks.
- In doc/repository-spec-v2.md, integrate package attestation uploads directly into ## Publishing Packages. - Document optional attestationUrl and attestationFields returned by GET /api/packages/versions/new. - Update lib/src/command/lish.dart to upload to attestationUrl with attestationFields prior to archive upload, without mutating form fields. - Update test/lish/utils.dart and test/lish/publishing_to_and_from_archive_test.dart to verify new upload flow and server compatibility error.
…n upload and clean up style
- In repository-spec-v2.md, specify that attestations are sent via HTTP POST to finalize-upload-url with body {'attestation': <bundle>}, and 405 Method Not Allowed indicates lack of support.
- In lib/src/command/lish.dart, remove separate multipart form upload for attestation and send POST request on finalization if attestation is provided.
- Add client-side validation of attestation file (non-empty, valid JSON object) before publishing.
- Handle 405 Method Not Allowed cleanly.
- Update and add tests in test/lish/publishing_to_and_from_archive_test.dart.
Instead of overloading <finalize-upload-url> with a POST carrying the attestation, the repository advertises support for publishing with attestations by returning an <attestation-upload-url> in the 'attestationUrl' property of the response from '/api/packages/versions/new'. The attestation is POSTed there before the package archive is uploaded, and <finalize-upload-url> remains a plain GET. This means: * a client with an attestation learns that the repository does not support attestations before uploading the archive, rather than from a 405 after the archive has been uploaded, and * the specification no longer has a GET and a POST doing the same thing on the same URL, which also avoids the conflict with following the 303 redirect to <finalize-upload-url> (a 303 is followed with a GET, dropping the body). The 'attestationUrl' is an opaque URL: a repository may point it at its own endpoint or at a signed URL on a storage service.
The repository may direct the attestation upload to another origin (e.g. a storage service). A non-2xx response from there did not match the repository's origin and was rethrown as an unexpected error. Track the attestation URL like the cloud storage URL and fail with the response body and `Failed to upload the attestation.` instead.
Adds client support for publishing package attestations during
pub publish:--with-attestation <bundle.sigstore.json>flag todart pub publish --from-archive.pubspec.yaml(repositoryfield) when publishing with an attestation.--with-attestationcan only be used with--from-archive, and that the file is non-empty and contains a JSON object, before asking the user to confirm the publication.test/lish/publishing_to_and_from_archive_test.dart.Protocol
doc/repository-spec-v2.mdgets a new Publishing with an Attestation section:<attestation-upload-url>in theattestationUrlproperty of the response from<hosted-url>/api/packages/versions/new. A repository that does not support it simply omits the property.POSTs the bundle asapplication/jsonto<attestation-upload-url>before uploading the package archive, attachingAuthorizationunder the same prefix rule asarchive_url.<finalize-upload-url>remains a plainGET.attestationUrlis an opaque URL: a repository may point it at its own endpoint or at a signed URL on a storage service. On pub.dev it is an endpoint on the app server, so no second signed upload policy is needed (see dart-lang/pub-dev#9545).The alternative considered was to
POSTthe attestation to<finalize-upload-url>and use405 Method Not Allowedfor feature detection. A separate URL was chosen because:405, which proxies and frameworks do not produce consistently, and303 See Otherpointing at<finalize-upload-url>, and a303is followed with aGET— any client that simply follows the redirect would drop the attestation body.Server side: dart-lang/pub-dev#9545